
#include "stdafx.h"
#include "resource.h"
#include "Ext2MgrDlg.h"

/* global management information */

BOOLEAN g_bAutoMount = 0;

ULONG g_nFlps     = 0;
ULONG g_nDisks    = 0;
ULONG g_nCdroms   = 0;
ULONG g_nVols     = 0;

EXT2_LETTER drvLetters[26];
EXT2_LETTER drvDigits[10];

ULONGLONG Ext2DrvLetters[2];

PEXT2_DISK      gDisks  = NULL;
PEXT2_CDROM     gCdroms = NULL;
PEXT2_VOLUME    gVols = NULL;

/* information string array */

typedef struct {
    int PartitionType;
    char *type;
} PARTITION_LIST;

typedef struct {
    UINT DriveType;
    char *type;
} DRIVE_LIST;

typedef struct {
    DEVICE_TYPE DeviceType;
    char *type;
} DEVICE_LIST;

typedef struct {
    STORAGE_BUS_TYPE BusType;
    char *type;
} BUSTYPE_LIST;

CHAR *
IrpMjStrings[] = {
    "IRP_MJ_CREATE",
    "IRP_MJ_CREATE_NAMED_PIPE",
    "IRP_MJ_CLOSE",
    "IRP_MJ_READ",
    "IRP_MJ_WRITE",
    "IRP_MJ_QUERY_INFORMATION",
    "IRP_MJ_SET_INFORMATION",
    "IRP_MJ_QUERY_EA",
    "IRP_MJ_SET_EA",
    "IRP_MJ_FLUSH_BUFFERS",
    "IRP_MJ_QUERY_VOLUME_INFORMATION",
    "IRP_MJ_SET_VOLUME_INFORMATION",
    "IRP_MJ_DIRECTORY_CONTROL",
    "IRP_MJ_FILE_SYSTEM_CONTROL",
    "IRP_MJ_DEVICE_CONTROL",
    "IRP_MJ_INTERNAL_DEVICE_CONTROL",
    "IRP_MJ_SHUTDOWN",
    "IRP_MJ_LOCK_CONTROL",
    "IRP_MJ_CLEANUP",
    "IRP_MJ_CREATE_MAILSLOT",
    "IRP_MJ_QUERY_SECURITY",
    "IRP_MJ_SET_SECURITY",
    "IRP_MJ_POWER",
    "IRP_MJ_SYSTEM_CONTROL",
    "IRP_MJ_DEVICE_CHANGE",
    "IRP_MJ_QUERY_QUOTA",
    "IRP_MJ_SET_QUOTA",
    "IRP_MJ_PNP"
};

CHAR *
PerfStatStrings[] = {
    "IRP_CONTEXT",
    "VCB",
    "FCB",
    "CCB",
    "MCB",
    "EXTENT",
    "RW_CONTEXT",
    "VPB",
    "FCB_NAME",
    "MCB_NAME",
    "FILE_NAME",
    "DIR_ENTRY",
    "DIR_PATTERN",
    "DISK_EVENT",
    "DISK_BUFFER",
    "BLOCK_DATA",
    "inode",
    "dentry",
    "buffer head",
    NULL
};

PARTITION_LIST PartitionList[] = {
    {PARTITION_ENTRY_UNUSED ,   "Empty"},
    {PARTITION_FAT_12 ,         "FAT12"},   /* 01 */
    {PARTITION_XENIX_1 ,        "Xenix-1"}, /* 02 */
    {PARTITION_XENIX_2 ,        "Xenix-2"},
    {PARTITION_FAT_16 ,         "FAT16"},   /* 04 */
    {PARTITION_EXTENDED ,       "Extended"},
    {PARTITION_HUGE ,           "FAT16 HUGE"}, /* 06*/
    {PARTITION_IFS ,            "HPFS/NTFS"},     /* 07 */

    {PARTITION_OS2BOOTMGR,      "OS/2"},    /* 0A */
    {PARTITION_FAT32 ,          "FAT32"},   /* 0B */
    {PARTITION_FAT32_XINT13 ,   "FAT32X"},  /* 0C*/
    {PARTITION_XINT13 ,         "XINT13"},  /* 0E */
    {PARTITION_XINT13_EXTENDED ,"EXINT13"}, /* 0F */
    {0x11 ,                     "Hidden FAT12"},
    {0x14 ,                     "Hidden FAT16"},
    {0x16 ,                     "Hidden FAT16"},
    {0x17 ,                     "Hidden HPFS/NTFS"},

    {0x1B ,                     "Hidden FAT32"},
    {0x1C ,                     "Hidden FAT32X"},

    {PARTITION_PREP ,           "OS/2"},    /* 41 */
    {PARTITION_LDM ,            "LDM"},     /* 42 */

    {0x52 ,                     "CP/M"},

    {PARTITION_UNIX ,           "UNIX"},    /* 63 */

    {PARTITION_NTFT ,           "NTFT"},    /* 80 */
    {0x81,                      "Minix"},
    {0x82,                      "Linux swap"},
    {0x83,                      "Linux"},

    {0x85,                      "Linux extend"},

    {0x8e,                      "Linux LVM"},

    {0xa5,                      "FreeBSD"},
    {0xa6,                      "OpenBSD"},

    {0xa8,                      "Darwin UFS"},
    {0xa9,                      "NetBSD"},

    {0xbe,                      "Solaris Boot"},
    {0xbf,                      "Solaris"},

    {VALID_NTFT ,               "VNTFT"},   /* C0 */
    {-1 ,"UNKNOWN"}
};

DRIVE_LIST DriveList[] = {
    {DRIVE_UNKNOWN,     "Unkown"},
    {DRIVE_NO_ROOT_DIR, "NoRoot"},
    {DRIVE_REMOVABLE,   "Removable"},
    {DRIVE_FIXED,       "Fixed"},
    {DRIVE_REMOTE,      "Remote"},
    {DRIVE_CDROM,       "CDROM"},
    {DRIVE_RAMDISK,     "RAMdisk"},
    {(UINT)-1,          "Invalid"}
};

BUSTYPE_LIST BusTypeList[] = {
    {BusTypeUnknown,    "Unkown"},
    {BusTypeScsi,       "SCSI"},
    {BusTypeAtapi,      "ATAPI"},
    {BusTypeAta,        "ATA"},
    {BusType1394,       "1394"},
    {BusTypeSsa,        "Ssa"},
    {BusTypeFibre,      "Fibre"},
    {BusTypeUsb,        "USB"},
    {BusTypeRAID,       "RAID"},
    {(STORAGE_BUS_TYPE)-1, "Invalid"}
};


DEVICE_LIST DeviceList[] = {
    {FILE_DEVICE_8042_PORT          ,"8042_PORT"},
    {FILE_DEVICE_ACPI               ,"ACPI"},
    {FILE_DEVICE_BATTERY            ,"BATTERY"},
    {FILE_DEVICE_BEEP               ,"BEEP"},
    {FILE_DEVICE_BUS_EXTENDER       ,"BUS_EXTENDER"},
    {FILE_DEVICE_CD_ROM             ,"CD_ROM"},
    {FILE_DEVICE_CD_ROM_FILE_SYSTEM ,"CD_ROM_FILE_SYSTEM"},
    {FILE_DEVICE_CHANGER            ,"CHANGER"},
    {FILE_DEVICE_CONTROLLER         ,"CONTROLLER"},
    {FILE_DEVICE_DATALINK           ,"DATALINK"},
    {FILE_DEVICE_DFS                ,"DFS"},
    {FILE_DEVICE_DFS_FILE_SYSTEM    ,"DFS_FILE_SYSTEM"},
    {FILE_DEVICE_DFS_VOLUME         ,"DFS_VOLUME"},
    {FILE_DEVICE_DISK               ,"DISK"},
    {FILE_DEVICE_DISK_FILE_SYSTEM   ,"DISK_FILE_SYSTEM"},
    {FILE_DEVICE_DVD                ,"DVD"},
    {FILE_DEVICE_FILE_SYSTEM        ,"FILE_SYSTEM"},
    {0x0000003a /*FILE_DEVICE_FIPS*/ ,"FIPS"},
    {FILE_DEVICE_FULLSCREEN_VIDEO   ,"FULLSCREEN_VIDEO"},
    {FILE_DEVICE_INPORT_PORT        ,"INPORT_PORT"},
    {FILE_DEVICE_KEYBOARD           ,"KEYBOARD"},
    {FILE_DEVICE_KS                 ,"KS"},
    {FILE_DEVICE_KSEC               ,"KSEC"},
    {FILE_DEVICE_MAILSLOT           ,"MAILSLOT"},
    {FILE_DEVICE_MASS_STORAGE       ,"MASS_STORAGE"},
    {FILE_DEVICE_MIDI_IN            ,"MIDI_IN"},
    {FILE_DEVICE_MIDI_OUT           ,"MIDI_OUT"},
    {FILE_DEVICE_MODEM              ,"MODEM"},
    {FILE_DEVICE_MOUSE              ,"MOUSE"},
    {FILE_DEVICE_MULTI_UNC_PROVIDER ,"MULTI_UNC_PROVIDER"},
    {FILE_DEVICE_NAMED_PIPE         ,"NAMED_PIPE"},
    {FILE_DEVICE_NETWORK            ,"NETWORK"},
    {FILE_DEVICE_NETWORK_BROWSER    ,"NETWORK_BROWSER"},
    {FILE_DEVICE_NETWORK_FILE_SYSTEM,"NETWORK_FILE_SYSTEM"},
    {FILE_DEVICE_NETWORK_REDIRECTOR ,"NETWORK_REDIRECTOR"},
    {FILE_DEVICE_NULL               ,"NULL"},
    {FILE_DEVICE_PARALLEL_PORT      ,"PARALLEL_PORT"},
    {FILE_DEVICE_PHYSICAL_NETCARD   ,"PHYSICAL_NETCARD"},
    {FILE_DEVICE_PRINTER            ,"PRINTER"},
    {FILE_DEVICE_SCANNER            ,"SCANNER"},
    {FILE_DEVICE_SCREEN             ,"SCREEN"},
    {FILE_DEVICE_SERENUM            ,"SERENUM"},
    {FILE_DEVICE_SERIAL_MOUSE_PORT  ,"SERIAL_MOUSE_PORT"},
    {FILE_DEVICE_SERIAL_PORT        ,"SERIAL_PORT"},
    {FILE_DEVICE_SMARTCARD          ,"SMARTCARD"},
    {FILE_DEVICE_SMB                ,"SMB"},
    {FILE_DEVICE_SOUND              ,"SOUND"},
    {FILE_DEVICE_STREAMS            ,"STREAMS"},
    {FILE_DEVICE_TAPE               ,"TAPE"},
    {FILE_DEVICE_TAPE_FILE_SYSTEM   ,"TAPE_FILE_SYSTEM"},
    {FILE_DEVICE_TERMSRV            ,"TERMSRV"},
    {FILE_DEVICE_TRANSPORT          ,"TRANSPORT"},
    {FILE_DEVICE_UNKNOWN            ,"UNKNOWN"},
    {FILE_DEVICE_VDM                ,"VDM"},
    {FILE_DEVICE_VIDEO              ,"VIDEO"},
    {FILE_DEVICE_VIRTUAL_DISK       ,"VIRTUAL_DISK"},
    {FILE_DEVICE_WAVE_IN            ,"WAVE_IN"},
    {FILE_DEVICE_WAVE_OUT           ,"WAVE_OUT"},
    {(DEVICE_TYPE)-1                ,"UNKNOWN"}
};

char *PartitionString(int type)
{
    PARTITION_LIST *p = PartitionList;

    while ( p->PartitionType != -1 ) {
        if ( type == p->PartitionType ) {
            return p->type;
        }
        p++;
    }
    return p->type;
}

char *DriveTypeString(UINT media)
{
    DRIVE_LIST *p = DriveList;

    while ( p->DriveType != (UINT)-1 ) {
        if ( media == p->DriveType ) {
            return p->type;
        }
        p++;
    }
    return p->type;
}

char *DeviceTypeString(DEVICE_TYPE media)
{
    DEVICE_LIST *p = DeviceList;

    while ( p->DeviceType != (DEVICE_TYPE)-1 ) {
        if ( media == p->DeviceType ) {
            return p->type;
        }
        p++;
    }
    return p->type;
}

char *BusTypeString(STORAGE_BUS_TYPE BusType)
{
    BUSTYPE_LIST *p = BusTypeList;

    while ( p->BusType != (STORAGE_BUS_TYPE)-1 ) {
        if ( BusType == p->BusType ) {
            return p->type;
        }
        p++;
    }

    return p->type;
}

/* Ext2Fsd supported codepages */

CHAR * gCodepages[] =
{
    "default",
    "cp936",
    "gb2312",
    "utf8",
    "cp1251",
    "cp1255",
    "cp437",
    "cp737",
    "cp775",
    "cp850",
    "cp852",
    "cp855",
    "cp857",
    "cp860",
    "cp861",
    "cp862",
    "cp863",
    "cp864",
    "cp865",
    "cp866",
    "cp869",
    "cp874",
    "tis-620",
    "cp932",
    "euc-jp",
    "sjis",
    "cp949",
    "euc-kr",
    "cp950",
    "big5",
    "iso8859-1",
    "iso8859-13",
    "iso8859-14",
    "iso8859-15",
    "iso8859-2",
    "iso8859-3",
    "iso8859-4",
    "iso8859-5",
    "iso8859-6",
    "iso8859-7",
    "iso8859-8",
    "iso8859-9",
    "koi8-r",
    "koi8-u",
    "koi8-ru",
    "cp1250",
    "acsii",
    NULL
};

/* routines */

BOOLEAN IsVista()
{
    OSVERSIONINFO   OsVerInfo;

    OsVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    if (GetVersionEx(&OsVerInfo)) {

        if (OsVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
            if (OsVerInfo.dwMajorVersion == 6)
                if (OsVerInfo.dwBuildNumber > 3790)
                    return TRUE;
    }

    return FALSE;
}

BOOLEAN
IsWindows2000()
{
    OSVERSIONINFO OsVer;
    memset(&OsVer, 0, sizeof(OsVer));
    OsVer.dwOSVersionInfoSize = sizeof(OsVer);

    if (GetVersionEx(&OsVer)) {
        if (OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT &&
                OsVer.dwMajorVersion <= 5 &&
                OsVer.dwMinorVersion == 0) {
            return TRUE;
        }
    } else {
        return TRUE;
    }

    return FALSE;
}


/*
 * Ext2LockVolume
 *     Lock the volume ...
 *
 * ARGUMENTS:
 *     VolumeHandle:    Volume handle.
 *
 * RETURNS:
 *     Success or Fail
 *
 * NOTES:
 *     N/A
 */


NT::NTSTATUS
Ext2LockVolume(HANDLE Handle)
{
    NT::NTSTATUS status;
    NT::IO_STATUS_BLOCK ioSb;

    status = NT::ZwFsControlFile(
                 Handle, NULL, NULL, NULL, &ioSb,
                 FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0);
    return status;
}


NT::NTSTATUS
Ext2UnLockVolume(HANDLE Handle)
{
    NT::NTSTATUS status;
    NT::IO_STATUS_BLOCK ioSb;

    status = NT::ZwFsControlFile(
                 Handle, NULL, NULL, NULL, &ioSb,
                 FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0);
    return status;
}


NT::NTSTATUS
Ext2DisMountVolume(HANDLE Handle)
{
    NT::NTSTATUS status;
    NT::IO_STATUS_BLOCK ioSb;

    status = NT::ZwFsControlFile(
                 Handle, NULL, NULL, NULL, &ioSb,
                 FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0);

    return status;
}

PDRIVE_LAYOUT_INFORMATION_EXT
Ext2QueryDriveLayout(
    HANDLE Handle,
    PUCHAR NumOfParts
)
{
    NT::IO_STATUS_BLOCK       ioSb;
    NT::NTSTATUS              status = STATUS_SUCCESS;

    PDRIVE_LAYOUT_INFORMATION_EXT driveLayout = NULL;
    ULONG                     dataSize = 256;
    ULONG                     retSize = 0;

QueryDrive:

    driveLayout = (PDRIVE_LAYOUT_INFORMATION_EXT) malloc(dataSize);
    if (!driveLayout) {
        goto errorout;
    }
    memset(driveLayout, 0, dataSize);

    if (IsWindows2000()) {

        PDRIVE_LAYOUT_INFORMATION oldLayout =
            (PDRIVE_LAYOUT_INFORMATION ) malloc(dataSize);
        if (!oldLayout) {
            goto errorout;
        }

        status = NT::ZwDeviceIoControlFile(
                     Handle, NULL, NULL, NULL, &ioSb,
                     IOCTL_DISK_GET_DRIVE_LAYOUT,
                     NULL, 0, (PVOID)oldLayout, dataSize
                 );

        if (NT_SUCCESS(status)) {

            ULONG newLayoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry);
            newLayoutSize += sizeof(PARTITION_INFORMATION_EX) * oldLayout->PartitionCount;

            if (dataSize >= newLayoutSize) {

                driveLayout->PartitionStyle = PARTITION_STYLE_MBR;
                driveLayout->PartitionCount = oldLayout->PartitionCount;
                driveLayout->Mbr.Signature = oldLayout->Signature;

                for (DWORD i=0; i < oldLayout->PartitionCount; i++) {
                    driveLayout->PartitionEntry[i].PartitionStyle = PARTITION_STYLE_MBR;

                    driveLayout->PartitionEntry[i].StartingOffset =
                        oldLayout->PartitionEntry[i].StartingOffset;
                    driveLayout->PartitionEntry[i].PartitionLength =
                        oldLayout->PartitionEntry[i].PartitionLength;
                    driveLayout->PartitionEntry[i].PartitionNumber =
                        oldLayout->PartitionEntry[i].PartitionNumber;
                    driveLayout->PartitionEntry[i].RewritePartition =
                        oldLayout->PartitionEntry[i].RewritePartition;
                    driveLayout->PartitionEntry[i].Mbr.PartitionType =
                        oldLayout->PartitionEntry[i].PartitionType;
                    driveLayout->PartitionEntry[i].Mbr.BootIndicator =
                        oldLayout->PartitionEntry[i].BootIndicator;
                    driveLayout->PartitionEntry[i].Mbr.RecognizedPartition =
                        oldLayout->PartitionEntry[i].RecognizedPartition;
                    driveLayout->PartitionEntry[i].Mbr.HiddenSectors =
                        oldLayout->PartitionEntry[i].HiddenSectors;
                }

            } else {
                dataSize = newLayoutSize;
                status = STATUS_BUFFER_TOO_SMALL;
            }
        }

        free(oldLayout);

    } else {

        status = NT::ZwDeviceIoControlFile(
                     Handle, NULL, NULL, NULL, &ioSb,
                     IOCTL_DISK_GET_DRIVE_LAYOUT_EXT,
                     NULL, 0, (PVOID)driveLayout, dataSize
                 );
    }

    if (status == STATUS_BUFFER_TOO_SMALL) {
        free(driveLayout);
        driveLayout = NULL;
        dataSize += 128;
        goto QueryDrive;
    }

    if (!NT_SUCCESS(status)) {
        free(driveLayout);
        driveLayout = NULL;
        goto errorout;
    }

    retSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EXT, PartitionEntry);
    retSize += sizeof(PARTITION_INFORMATION) *  driveLayout->PartitionCount;
    ASSERT(ioSb.Information == retSize);

    if (driveLayout->PartitionStyle == PARTITION_STYLE_MBR) {

        PPARTITION_INFORMATION_EXT  Part;
        UCHAR i = 0, cnt = 0;

        /* Now walk the Drive_Layout to count the partitions */
        while (i < (UCHAR)driveLayout->PartitionCount) {
            Part = &driveLayout->PartitionEntry[i++];
            if (Part->Mbr.PartitionType != PARTITION_ENTRY_UNUSED &&
                    Part->Mbr.PartitionType != PARTITION_EXTENDED &&
                    Part->Mbr.PartitionType != PARTITION_XINT13_EXTENDED) {
                cnt++;
            }
        }
        *NumOfParts = cnt;
    } else if (driveLayout->PartitionStyle == PARTITION_STYLE_GPT) {
        *NumOfParts = (UCHAR)driveLayout->PartitionCount;
    } else {
        *NumOfParts = 0;
        free(driveLayout);
        driveLayout = NULL;
    }

    if (*NumOfParts == 0) {
        free(driveLayout);
        driveLayout = NULL;
    }


errorout:

    return driveLayout;
}

NTSTATUS
Ext2SetDriveLayout(
    HANDLE  Handle,
    PDRIVE_LAYOUT_INFORMATION_EXT Layout
)
{
    NT::IO_STATUS_BLOCK       ioSb;
    NT::NTSTATUS              status = STATUS_SUCCESS;
    ULONG                     dataSize;

    dataSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EXT, PartitionEntry);
    dataSize += sizeof(PARTITION_INFORMATION_EXT) * Layout->PartitionCount;

    if (IsWindows2000()) {

        if (Layout->PartitionStyle != PARTITION_STYLE_MBR) {
            return STATUS_UNSUCCESSFUL;
        }

        ULONG newLayoutSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry);
        newLayoutSize += sizeof(PARTITION_INFORMATION) * Layout->PartitionCount;

        PDRIVE_LAYOUT_INFORMATION oldLayout =
            (PDRIVE_LAYOUT_INFORMATION ) malloc(newLayoutSize);
        if (!oldLayout) {
            goto errorout;
        }

        oldLayout->PartitionCount = Layout->PartitionCount;
        oldLayout->Signature = Layout->Mbr.Signature;

        for (DWORD i=0; i < oldLayout->PartitionCount; i++) {

            oldLayout->PartitionEntry[i].StartingOffset =
                Layout->PartitionEntry[i].StartingOffset;
            oldLayout->PartitionEntry[i].PartitionLength =
                Layout->PartitionEntry[i].PartitionLength;
            oldLayout->PartitionEntry[i].PartitionNumber =
                Layout->PartitionEntry[i].PartitionNumber;
            oldLayout->PartitionEntry[i].RewritePartition =
                Layout->PartitionEntry[i].RewritePartition;
            oldLayout->PartitionEntry[i].PartitionType =
                Layout->PartitionEntry[i].Mbr.PartitionType;
            oldLayout->PartitionEntry[i].BootIndicator =
                Layout->PartitionEntry[i].Mbr.BootIndicator;
            oldLayout->PartitionEntry[i].RecognizedPartition =
                Layout->PartitionEntry[i].Mbr.RecognizedPartition;
            oldLayout->PartitionEntry[i].HiddenSectors =
                Layout->PartitionEntry[i].Mbr.HiddenSectors;
        }

        status = NT::ZwDeviceIoControlFile(
                     Handle, NULL, NULL, NULL, &ioSb,
                     IOCTL_DISK_SET_DRIVE_LAYOUT,
                     (PVOID)oldLayout, newLayoutSize,
                     NULL, 0
                 );

        free(oldLayout);

    } else {

        status = NT::ZwDeviceIoControlFile(
                     Handle, NULL, NULL, NULL, &ioSb,
                     IOCTL_DISK_SET_DRIVE_LAYOUT_EXT,
                     (PVOID)Layout, dataSize,NULL, 0
                 );
    }

    return status;

errorout:

    return STATUS_UNSUCCESSFUL;
}

BOOLEAN
Ext2SetPartitionType(
    PEXT2_PARTITION Part,
    BYTE            Type
)
{
    BOOLEAN rc = FALSE;
    HANDLE  Handle = NULL;
    NT::NTSTATUS status;

    UCHAR  NumParts = 0;
    PDRIVE_LAYOUT_INFORMATION_EXT Layout = NULL;

    DWORD i;

    status = Ext2Open(Part->Disk->Name, &Handle,
                      EXT2_DESIRED_ACCESS | GENERIC_WRITE);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    Layout = Part->Disk->Layout;

    if (!Layout) {
        goto errorout;
    }

    if (Layout->PartitionStyle != PARTITION_STYLE_MBR) {
        goto errorout;
    }

    for (i=0; i < Layout->PartitionCount; i++) {

        if ((Layout->PartitionEntry[i].StartingOffset.QuadPart ==
                Part->Entry->StartingOffset.QuadPart) &&
                (Layout->PartitionEntry[i].PartitionLength.QuadPart ==
                 Part->Entry->PartitionLength.QuadPart) &&
                (Layout->PartitionEntry[i].PartitionNumber ==
                 Part->Entry->PartitionNumber) ) {

            Layout->PartitionEntry[i].Mbr.PartitionType = Type;
            Layout->PartitionEntry[i].RewritePartition = TRUE;

            rc = TRUE;
            break;
        }
    }

    if (!rc) {
        goto errorout;
    }

    status = Ext2SetDriveLayout(Handle, Layout);
    rc = NT_SUCCESS(status);

errorout:

    Ext2Close(&Handle);

    return rc;
}


BOOLEAN
Ext2FlushVolume(CHAR *Device)
{
    HANDLE  handle = NULL;
    NT::NTSTATUS status;
    NT::IO_STATUS_BLOCK iosb;

    status = Ext2Open(Device, &handle, EXT2_DESIRED_ACCESS | GENERIC_WRITE);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    status = NT::ZwFlushBuffersFile(handle, &iosb);

errorout:

    if (handle) {
        Ext2Close(&handle);
    }

    return NT_SUCCESS(status);
}

PEXT2_PARTITION
Ext2QueryVolumePartition(
    PEXT2_VOLUME    Volume
)
{
    PEXT2_PARTITION Part = NULL;
    DWORD i, j;

    for (i=0; i < g_nDisks; i++) {
        for (j=0; j < (int)gDisks[i].NumParts; j++) {
            if (gDisks[i].DataParts[j].Volume == Volume) {
                Part = &gDisks[i].DataParts[j];
                break;
            }
        }
    }

    return Part;
}

NT::NTSTATUS
Ext2QueryProperty(
    HANDLE                      Handle,
    STORAGE_PROPERTY_ID         Id,
    PVOID                       DescBuf,
    ULONG                       DescSize
)
{
    STORAGE_PROPERTY_QUERY	    SPQ;
    NT::NTSTATUS                status;
    NT::IO_STATUS_BLOCK         ioSb;

    SPQ.PropertyId = Id;
    SPQ.QueryType  = PropertyStandardQuery;

    memset(DescBuf, 0, DescSize);

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &ioSb,
                 IOCTL_STORAGE_QUERY_PROPERTY,
                 &SPQ, sizeof(STORAGE_PROPERTY_QUERY),
                 DescBuf,  DescSize
             );

    return status;
}

/*
 * Ext2QueryDisk
 *     Get volume gemmetry information ...
 *
 * ARGUMENTS:
 *     VolumeHandle:    Volume handle.
 *
 * RETURNS:
 *     Success or Fail
 *
 * NOTES:
 *     N/A
 */

NTSTATUS
Ext2QueryDisk(
    HANDLE                  Handle,
    PDISK_GEOMETRY          DiskGeometry
)
{
    NT::NTSTATUS        status;
    NT::IO_STATUS_BLOCK ioSb;

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &ioSb,
                 IOCTL_DISK_GET_DRIVE_GEOMETRY,
                 DiskGeometry, sizeof(DISK_GEOMETRY),
                 DiskGeometry, sizeof(DISK_GEOMETRY));


    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

errorout:

    return status;
}

PVOLUME_DISK_EXTENTS
Ext2QueryVolumeExtents(HANDLE hVolume)
{
    ULONG	dataSize = 1024;
    PVOLUME_DISK_EXTENTS dskExtents = NULL;

    NT::NTSTATUS        status;
    NT::IO_STATUS_BLOCK ioSb;

QueryExtent:

    dskExtents = (PVOLUME_DISK_EXTENTS)malloc(dataSize);
    if (NULL == dskExtents) {
        goto errorout;
    }

    status = NT::ZwDeviceIoControlFile(
                 hVolume, NULL, NULL, NULL, &ioSb,
                 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
                 NULL, 0, dskExtents, dataSize );

    if (status == STATUS_BUFFER_TOO_SMALL) {
        free(dskExtents);
        dskExtents = NULL;
        dataSize += 1024;
        goto QueryExtent;
    }

    if (!NT_SUCCESS(status)) {
        free(dskExtents);
        dskExtents = NULL;
        goto errorout;
    }

errorout:

    return dskExtents;
}

PSTORAGE_DEVICE_NUMBER
Ext2QueryDeviceNumber(HANDLE hVolume)
{
    ULONG	            dataSize;
    PSTORAGE_DEVICE_NUMBER SDN = NULL;

    NT::NTSTATUS        status;
    NT::IO_STATUS_BLOCK ioSb;

    dataSize = sizeof(STORAGE_DEVICE_NUMBER);

QuerySDN:

    SDN = (PSTORAGE_DEVICE_NUMBER)malloc(dataSize);
    if (NULL == SDN) {
        goto errorout;
    }
    memset(SDN, 0, dataSize);

    status = NT::ZwDeviceIoControlFile(
                 hVolume, NULL, NULL, NULL, &ioSb,
                 IOCTL_STORAGE_GET_DEVICE_NUMBER,
                 NULL, 0, SDN, dataSize );

    if (status == STATUS_BUFFER_TOO_SMALL) {
        free(SDN);
        SDN = NULL;
        dataSize += sizeof(STORAGE_DEVICE_NUMBER);
        goto QuerySDN;
    }

    if (!NT_SUCCESS(status)) {
        free(SDN);
        SDN = NULL;
        goto errorout;
    }

errorout:

    return SDN;
}

BOOLEAN
Ext2QueryDrvLetter(
    PEXT2_LETTER    drvLetter
)
{
    CHAR	        drvPath[MAX_PATH];
    CHAR            devPath[] = "A:";
    HANDLE	        hVolume;
    NT::NTSTATUS    status = STATUS_SUCCESS;
    DWORD           rc = 0;

    devPath[0] = drvLetter->Letter;
    drvLetter->DrvType = GetDriveType(devPath);

    if (drvLetter->DrvType == DRIVE_NO_ROOT_DIR) {
        drvLetter->bUsed = FALSE;
        goto errorout;
    } else {
        drvLetter->bUsed = TRUE;
        QueryDosDevice(devPath, drvLetter->SymLink, MAX_PATH);
    }

    if (drvLetter->Letter == 'A' ||
            drvLetter->Letter == 'B' ) {
        drvLetter->bUsed = TRUE;
        goto errorout;
    }

    if (drvLetter->DrvType == DRIVE_REMOVABLE ||
            drvLetter->DrvType == DRIVE_FIXED) {

        sprintf(drvPath, "\\DosDevices\\%c:", drvLetter->Letter);
        status = Ext2Open(drvPath, &hVolume, EXT2_DESIRED_ACCESS);
        if (!NT_SUCCESS(status)) {
            goto errorout;
        }

        drvLetter->Extent = Ext2QueryVolumeExtents(hVolume);
        if (drvLetter->DrvType == DRIVE_REMOVABLE) {
            drvLetter->SDN = Ext2QueryDeviceNumber(hVolume);
        }

        Ext2Close(&hVolume);
    }

errorout:

    return NT_SUCCESS(status);
}

NTSTATUS
Ext2QueryMediaType(
    HANDLE                  Handle,
    PDWORD                  MediaType
)
{
    NT::NTSTATUS        status;
    NT::IO_STATUS_BLOCK ioSb;
    PGET_MEDIA_TYPES    mediaTypes;
    UCHAR               buffer[1024];

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &ioSb,
                 IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
                 NULL, 0, buffer, 1024 );


    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    mediaTypes = (PGET_MEDIA_TYPES) buffer;
    *MediaType = mediaTypes->DeviceType;

errorout:

    return status;
}

/*
 * Ext2Read
 *     Read data from disk or file ...
 *
 * ARGUMENTS:
 *     VolumeHandle: Volume Handle
 *     Offset      : Disk Offset
 *     Length      : Data Length to be read
 *     Buffer      : ...
 *
 * RETURNS:
 *     Success: STATUS_SUCCESS
 *     Fail:  ...
 *
 * NOTES:
 *     Both Length and Offset should be SECTOR_SIZE aligned.
 */
NT::NTSTATUS
Ext2Read(
    IN  HANDLE          Handle,
    IN  BOOLEAN         IsFile,
    IN  ULONG           SectorSize,
    IN  ULONGLONG       Offset,
    IN  ULONG           Length,
    IN  PVOID           Buffer
)
{
    NT::NTSTATUS        status;
    NT::IO_STATUS_BLOCK ioSb;
    NT::LARGE_INTEGER   address;
    ULONG               aLength = 0;
    PVOID               aBuffer = NULL;

    if (SectorSize == 0 || SectorSize == 1)
        IsFile = TRUE;

    ASSERT(Buffer != NULL);

    if (IsFile) {

        address.QuadPart = Offset;
        status = NT::ZwReadFile(
                     Handle, 0, NULL, NULL, &ioSb,
                     Buffer, Length, &address, NULL
                 );
    } else {

        address.QuadPart = Offset & (~((ULONGLONG)SectorSize - 1));
        aLength  = (Length + SectorSize - 1)&(~(SectorSize - 1));
        aLength += ((ULONG)(Offset - address.QuadPart) + SectorSize - 1)
                   & (~(SectorSize - 1));

        aBuffer = malloc(aLength);
        if (!aBuffer) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto errorout;
        }

        status = NT::ZwReadFile(
                     Handle, 0, NULL, NULL, &ioSb,
                     aBuffer, aLength, &address, NULL
                 );

        if (!NT_SUCCESS(status)) {
            goto errorout;
        }

        memmove( Buffer, (PUCHAR)aBuffer +
                 (ULONG)(Offset - address.QuadPart), Length);
    }

errorout:

    if (aBuffer)
        free(aBuffer);

    return status;
}

/*
 * Ext2Write
 *     Write data to disk or file ...
 *
 * ARGUMENTS:
 *     VolumeHandle: Volume Handle
 *     Offset      : Disk Offset
 *     Length      : Data Length to be written
 *     Buffer      : Data to be written ...
 *
 * RETURNS:
 *     Success: STATUS_SUCCESS
 *     Fail:  ...
 *
 * NOTES:
 *     Both Length and Offset should be SECTOR_SIZE aligned.
 */

NT::NTSTATUS
Ext2WriteDisk(
    HANDLE          Handle,
    BOOLEAN         IsFile,
    ULONG           SectorSize,
    ULONGLONG       Offset,
    ULONG           Length,
    PVOID           Buffer
)
{
    NT::LARGE_INTEGER       address;
    NT::NTSTATUS            status;
    NT::IO_STATUS_BLOCK     ioSb;

    ULONG                   aLength = 0;
    PVOID                   aBuffer = NULL;

    if (SectorSize == 0 || SectorSize == 1)
        IsFile = TRUE;

    ASSERT(Buffer != NULL);

    if (IsFile) {

        address.QuadPart = Offset;
        status = NT::ZwWriteFile(
                     Handle, 0, NULL, NULL, &ioSb,
                     Buffer, Length, &address, NULL
                 );
    } else  {

        address.QuadPart = Offset & (~((ULONGLONG)SectorSize - 1));
        aLength = (Length + SectorSize - 1)&(~(SectorSize - 1));
        aLength += ((ULONG)(Offset - address.QuadPart) + SectorSize - 1)
                   & (~(SectorSize - 1));

        aBuffer = malloc(aLength);
        if (!aBuffer) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto errorout;
        }

        if ( (aLength != Length) ||
                (address.QuadPart != (LONGLONG)Offset)) {
            status = NT::ZwReadFile(
                         Handle, 0, NULL, NULL, &ioSb,
                         aBuffer, aLength, &address, NULL
                     );

            if (!NT_SUCCESS(status)) {
                goto errorout;
            }
        }

        memmove((PUCHAR)aBuffer + (ULONG)(Offset - address.QuadPart),
                Buffer, Length);
        status = NT::ZwWriteFile(
                     Handle, 0, NULL, NULL, &ioSb,
                     aBuffer, aLength, &address, NULL
                 );
    }

errorout:

    if (aBuffer)
        free(aBuffer);

    return status;
}

NT::NTSTATUS
Ext2Open(
    PCHAR                   FileName,
    PHANDLE                 Handle,
    ULONG                   DesiredAccess
)
{
    NT::IO_STATUS_BLOCK     iosb;
    NT::NTSTATUS            status;
    NT::ANSI_STRING         AnsiFilespec;
    NT::UNICODE_STRING      UnicodeFilespec;
    NT::OBJECT_ATTRIBUTES   ObjectAttributes;

    SHORT                   UnicodeName[MAX_PATH];
    CHAR                    AnsiName[MAX_PATH];
    USHORT                  NameLength = 0;

    memset(UnicodeName, 0, sizeof(SHORT) * MAX_PATH);
    memset(AnsiName, 0, sizeof(UCHAR) * MAX_PATH);

    NameLength = strlen(FileName);
    ASSERT(NameLength < MAX_PATH);

    if (FileName[0] == '\\')  {
        memmove(AnsiName, FileName, NameLength);
    } else {
        memmove(&AnsiName[0], "\\DosDevices\\", 12);
        memmove(&AnsiName[12], FileName, NameLength);
        NameLength += 12;
    }

    AnsiFilespec.MaximumLength = AnsiFilespec.Length = NameLength;
    AnsiFilespec.Buffer = AnsiName;

    UnicodeFilespec.MaximumLength = MAX_PATH * 2;
    UnicodeFilespec.Length = 0;
    UnicodeFilespec.Buffer = (PWSTR)UnicodeName;

    NT::RtlAnsiStringToUnicodeString(&UnicodeFilespec, &AnsiFilespec, FALSE);

    //
    // Setup the name in an object attributes structure.
    // Note that we create a name that is case INsensitive
    //

    ObjectAttributes.Length = sizeof(NT::OBJECT_ATTRIBUTES);
    ObjectAttributes.RootDirectory = NULL;
    ObjectAttributes.Attributes = 0; /*OBJ_CASE_INSENSITIVE;*/
    ObjectAttributes.ObjectName = &UnicodeFilespec;
    ObjectAttributes.SecurityDescriptor = NULL;
    ObjectAttributes.SecurityQualityOfService = NULL;

    //
    // Do the create.  In this particular case, we'll have the I/O Manager
    // make our write requests syncrhonous for our convenience.
    //
    status = NT::ZwCreateFile(
                 Handle,                           // returned file handle
                 (DesiredAccess | SYNCHRONIZE),    // desired access
                 &ObjectAttributes,                // ptr to object attributes
                 &iosb,                            // ptr to I/O status block
                 0,                                // allocation size
                 FILE_ATTRIBUTE_NORMAL,            // file attributes
                 FILE_SHARE_WRITE | FILE_SHARE_READ, // share access
                 FILE_OPEN  /*FILE_SUPERSEDE*/,    // create disposition
                 FILE_SYNCHRONOUS_IO_NONALERT |    // create options
                 ((DesiredAccess & GENERIC_WRITE) ?
                  FILE_NO_INTERMEDIATE_BUFFERING : 0),
                 NULL,                             // ptr to extended attributes
                 0);                               // length of ea buffer

    //
    // Check the system service status
    //
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Check the returned status too...
    //
    if (!NT_SUCCESS(iosb.Status)) {
        return iosb.Status;
    }

    return status;
}

VOID
Ext2Close(HANDLE * Handle)
{
    NT::NTSTATUS status = 0;
    if (Handle != NULL && *Handle != 0 && *Handle != INVALID_HANDLE_VALUE) {
        status = NT::ZwClose(*Handle);
        if (NT_SUCCESS(status)) {
            *Handle = 0;
        } else {
            ::MessageBox(NULL, "Failed to close handle", "Ext2Close", MB_OK);
        }
    }
}

BOOLEAN
Ext2QuerySysConfig()
{
    NT::NTSTATUS status;
    NT::_SYSTEM_CONFIGURATION_INFORMATION ConfigInfo;

    status = NT::ZwQuerySystemInformation(
                 NT::SystemConfigurationInformation,
                 &ConfigInfo, sizeof(ConfigInfo), 0);
    if (NT_SUCCESS(status)) {
        g_nDisks  = ConfigInfo.DiskCount;
        g_nFlps   = ConfigInfo.FloppyCount;
        g_nCdroms = ConfigInfo.CdRomCount;
        return TRUE;
    }

    return FALSE;
}

BOOLEAN
Ext2LoadDisks()
{
    ULONG           i = 0, j = 0;
    NT::NTSTATUS    status;
    CHAR            drvName[MAX_PATH];

    if (g_nDisks == 0) {
        return FALSE;
    }

    gDisks = (PEXT2_DISK) malloc(sizeof(EXT2_DISK) * g_nDisks);
    if (gDisks == NULL) {
        return FALSE;
    }
    memset(gDisks, 0, sizeof(EXT2_DISK) * g_nDisks);

    while (i < g_nDisks && j < 256) {

        HANDLE  Handle = NULL;

        gDisks[i].Magic = EXT2_DISK_MAGIC;
        gDisks[i].Null  = EXT2_DISK_NULL_MAGIC;
        gDisks[i].OrderNo = (UCHAR) j;
        gDisks[i].bLoaded = FALSE;
        sprintf(drvName, "PhysicalDrive%d\0", j);
        if (QueryDosDevice(drvName, gDisks[i].Name, MAX_PATH) == 0) {
            sprintf(gDisks[i].Name, "\\DosDevices\\PhysicalDrive%d\0", j);
        }
        j++;

        status = Ext2Open(gDisks[i].Name, &Handle, EXT2_DESIRED_ACCESS);
        if (!NT_SUCCESS(status)) {
            continue;
        }

        status = Ext2QueryDisk(Handle, &gDisks[i].DiskGeometry);
        if (NT_SUCCESS(status)) {
            gDisks[i].bEjected = FALSE;
        } else {
            if (STATUS_NO_MEDIA_IN_DEVICE == status) {
                gDisks[i].bEjected = TRUE;
            } else {
                goto Next;
            }
        }

        status = Ext2QueryProperty( Handle, StorageDeviceProperty,
                                    (PVOID)&gDisks[i].SDD,
                                    sizeof(STORAGE_DEVICE_DESCRIPTOR)
                                  );
        if (!NT_SUCCESS(status)) {
            goto Next;
        }

        status = Ext2QueryProperty( Handle, StorageAdapterProperty,
                                    (PVOID)&gDisks[i].SAD,
                                    sizeof(STORAGE_ADAPTER_DESCRIPTOR)
                                  );
        if (!NT_SUCCESS(status)) {
            goto Next;
        }

        if (!gDisks[i].bEjected) {
            gDisks[i].Layout = Ext2QueryDriveLayout(
                                   Handle, &gDisks[i].NumParts);
        }

        gDisks[i].bLoaded = TRUE;

Next:

        Ext2Close(&Handle);
        i++;
    }

    g_nDisks = i;

    return TRUE;
}

CString
Ext2PartInformation(PEXT2_PARTITION part)
{
    CString     s, ret="";

    s.Format("\r\n    Partition No: %d\r\n\r\n", part->Number);
    if (!part->Entry) {
        return ret;
    }

    s = "      Partition Type: ";
    if (part->Entry->PartitionStyle == PARTITION_STYLE_MBR) {
        s += PartitionString(part->Entry->Mbr.PartitionType);
    } else {
        s += "GPT";
    }
    ret += s;
    ret += "\r\n";

    s.Format("      StartingOffset: %I64u\r\n", part->Entry->StartingOffset.QuadPart);
    ret += s;

    s.Format("      PartitionLength: %I64u\r\n", part->Entry->PartitionLength.QuadPart);
    ret += s;

    /* mount points */
    ret += "      MountPoints: ";
    ret += Ext2QueryVolumeLetterStrings(part->DrvLetters, NULL);
    ret += "\r\n";

    if (!part->Volume) {
        return ret;
    }

    if (part->Volume->bRecognized) {

        ULONGLONG   totalSize, freeSize;

        s.Format("      Filesystem: %s\r\n", part->Volume->FileSystem);
        ret += s;

        if ((part->Volume->EVP.bExt2 || part->Volume->EVP.bExt3)) {
            s = "      codepage:";
            s += part->Volume->EVP.Codepage;
            if (part->Volume->EVP.bReadonly) {
                s += ",Readonly";
            }
        }
        ret += s;
        ret += "\r\n";

        totalSize = part->Volume->FssInfo.TotalAllocationUnits.QuadPart;
        freeSize  = part->Volume->FssInfo.AvailableAllocationUnits.QuadPart;
        totalSize = totalSize * part->Volume->FssInfo.BytesPerSector *
                    part->Volume->FssInfo.SectorsPerAllocationUnit;
        freeSize  = freeSize * part->Volume->FssInfo.BytesPerSector *
                    part->Volume->FssInfo.SectorsPerAllocationUnit;
        s.Format("      Size: %I64u\r\n", totalSize);
        ret += s;

        s.Format("      Free: %I64u\r\n", freeSize);
        ret += s;
    }

    return ret;
}

CString
Ext2DiskInformation(PEXT2_DISK  disk)
{
    CString     s, ret="";

    s.Format("\r\nDisk %d: %s\r\n\r\n", disk->OrderNo, disk->Name);
    ret += s;

    if (disk->SDD.VendorIdOffset) {
        ret += "\r\n  VendorId: ";
        ret += (PCHAR)&disk->SDD + disk->SDD.VendorIdOffset;
        ret += "\r\n";
    }

    if (disk->SDD.ProductIdOffset) {
        ret += "    ProductId: ";
        ret += (PCHAR)&disk->SDD + disk->SDD.ProductIdOffset;
        ret += "\r\n";
    }

    if (disk->SDD.SerialNumberOffset) {
        ret += "    SerialNumber: ";
        ret += (PCHAR)&disk->SDD + disk->SDD.SerialNumberOffset;
        ret += "\r\n";
    }

    ret += "    BusType: ";
    ret += BusTypeString(disk->SDD.BusType);
    ret += "\r\n";

    ret += "    Media Type: ";
    if (disk->SDD.RemovableMedia) {
        ret += " Removable\r\n";
    } else {
        s = "RAW";
        if (disk->Layout) {
            if (disk->Layout->PartitionStyle == PARTITION_STYLE_MBR) {
                if (disk->Layout->PartitionEntry->Mbr.PartitionType
                        == PARTITION_LDM) {
                    s.LoadString(IDS_DISK_TYPE_DYN);
                } else {
                    s.LoadString(IDS_DISK_TYPE_BASIC);
                }
            } else if (disk->Layout->PartitionStyle == PARTITION_STYLE_MBR) {
                s = "GUID";
            }
        }
        s   += "\r\n";
        ret += s;
    }

    if (disk->bEjected) {
        ret += "    No media\r\n";
    } else {
        /* geometry information */
        ret +=   "    DiskGeometry Layout:\r\n";
        s.Format("      BytesPerSector = %u\r\n", disk->DiskGeometry.BytesPerSector);
        ret += s;
        s.Format("      SectorsPerTrack = %u\r\n", disk->DiskGeometry.SectorsPerTrack);
        ret += s;
        s.Format("      TracksPerCylinder = %u\r\n", disk->DiskGeometry.TracksPerCylinder);
        ret += s;
        s.Format("      Cylinderst = %I64u\r\n", disk->DiskGeometry.Cylinders.QuadPart);
        ret += s;

        switch (disk->DiskGeometry.MediaType) {
        case FixedMedia:
            s="Fixed";
            break;
        case RemovableMedia:
            s="Removable";
            break;
        case CD_ROM:
            s="CDROM";
            break;
        case CD_R:
            s="CDR";
            break;
        case CD_RW:
            s="CDRW";
            break;
        case DVD_ROM:
            s="DVD";
            break;
        case DVD_R:
            s="DVDR";
            break;
        case DVD_RW:
            s="DVDRW";
            break;
        default:
            s="Unkown";
        }

        ret += "    MediaType: ";
        ret += s;
        ret += "\r\n";
    }

    ret += "\r\n";
    if (disk->Layout) {
        s.Format("    Partition Numbers: %d\r\n", disk->NumParts);
        ret += s;

        for (UCHAR i=0; i < disk->NumParts; i++) {
            ret += Ext2PartInformation(&disk->DataParts[i]);
            ret += "\r\n";
        }
    }

    return ret;
}

CString
Ext2CdromInformation(PEXT2_CDROM cdrom)
{
    CString     s, ret="";

    s.Format("\r\nCdrom %d: %s\r\n\r\n", cdrom->OrderNo, cdrom->Name);
    ret += s;

    if (cdrom->SDD.VendorIdOffset) {
        ret += "    VendorId: ";
        ret += (PCHAR)&cdrom->SDD + cdrom->SDD.VendorIdOffset;
        ret += "\r\n";
    }

    if (cdrom->SDD.ProductIdOffset) {
        ret += "    ProductId: ";
        ret += (PCHAR)&cdrom->SDD + cdrom->SDD.ProductIdOffset;
        ret += "\r\n";
    }

    if (cdrom->SDD.SerialNumberOffset) {
        ret += "    SerialNumber: ";
        ret += (PCHAR)&cdrom->SDD + cdrom->SDD.SerialNumberOffset;
        ret += "\r\n";
    }

    ret += "    BusType: ";
    ret += BusTypeString(cdrom->SDD.BusType);
    ret += "\r\n";

    if (cdrom->bLoaded) {
        ret += "    Media Type: ";
        if (cdrom->bIsDVD) {
            s = "DVD\r\n";
        } else {
            s = "CDROM\r\n";
        }
        ret += s;

        ret +=   "    DiskGeometry Layout:\r\n";
        s.Format("      BytesPerSector = %u\r\n", cdrom->DiskGeometry.BytesPerSector);
        ret += s;
        s.Format("      SectorsPerTrack = %u\r\n", cdrom->DiskGeometry.SectorsPerTrack);
        ret += s;
        s.Format("      TracksPerCylinder = %u\r\n", cdrom->DiskGeometry.TracksPerCylinder);
        ret += s;
        s.Format("      Cylinderst = %I64u\r\n", cdrom->DiskGeometry.Cylinders.QuadPart);
        ret += s;
    } else {
        s = "    No media\r\n";
        ret += s;
    }

    if (cdrom->bLoaded) {
        if (cdrom->bEjected) {
            ret += "    Media ejected\r\n";
        } else {
            ret += "    File system: ";
            if (cdrom->EVP.bExt2) {
                s = "EXT";
                s += ('2' + cdrom->EVP.bExt3);
            } else {
                s = "CDFS";
            }
            ret += s;
            ret += "\r\n";

            s = "    Online,";
            switch (cdrom->DiskGeometry.MediaType) {
            case FixedMedia:
                s +="Fixed";
                break;
            case RemovableMedia:
                s += "Media Removable";
                break;
            case CD_ROM:
                s +=" CDROM";
                break;
            case CD_R:
                s += "CDR";
                break;
            case CD_RW:
                s += "CDRW";
                break;
            case DVD_ROM:
                s += "DVD";
                break;
            case DVD_R:
                s += "DVDR";
                break;
            case DVD_RW:
                s += "DVDRW";
                break;
            default:
                s += "Unkown";
            }
            ret += s;
            ret += "\r\n";
        }
    } else {
        ret += "    Device stopped\r\n";
    }

    ret += "    Mountpoints: ";
    ret += Ext2QueryVolumeLetterStrings(cdrom->DrvLetters, NULL);
    ret += "\r\n";

    return ret;
}

CString
Ext2VolumeInformation(PEXT2_VOLUME vol)
{
    CString s, ret = "";

    s.Format("\r\nVolume: %s:\r\n\r\n", vol->Name);
    ret += s;

    ret += "    Filesystem: ";
    ret += vol->FileSystem;
    ret += "\r\n";

    /* mount points */
    ret += "    Mountpoints: ";
    ret += Ext2QueryVolumeLetterStrings(vol->DrvLetters, NULL);
    ret += "\r\n";

    /* set volume status */
    s = "    Volume status: Online";
    if (vol->bRecognized && (vol->EVP.bExt2 || vol->EVP.bExt3)) {
        s += ",codepage:";
        s += vol->EVP.Codepage;
        if (vol->EVP.bReadonly) {
            s += ",Readonly";
        }
    }
    ret += s;
    ret += "\r\n";

    {
        ULONGLONG   totalSize, freeSize;
        totalSize = vol->FssInfo.TotalAllocationUnits.QuadPart;
        freeSize  = vol->FssInfo.AvailableAllocationUnits.QuadPart;
        totalSize = totalSize * vol->FssInfo.BytesPerSector *
                    vol->FssInfo.SectorsPerAllocationUnit;
        freeSize  = freeSize * vol->FssInfo.BytesPerSector *
                    vol->FssInfo.SectorsPerAllocationUnit;
        s.Format("    size: %I64u\r\n", totalSize);
        ret += s;

        s.Format("    free space: %I64u\r\n", freeSize);
        ret += s;
    }

    if (vol->Extent) {
        for (DWORD i=0; i < vol->Extent->NumberOfDiskExtents; i++) {
            s.Format("    Extent: %d\r\n", i);
            ret += s;
            s.Format("      DiskNumber: %d\r\n", vol->Extent->Extents[i].DiskNumber);
            ret += s;
            s.Format("      StartingOffset: %I64u\r\n", vol->Extent->Extents[i].StartingOffset.QuadPart);
            ret += s;
            s.Format("      ExtentLength: %I64u\r\n", vol->Extent->Extents[i].ExtentLength.QuadPart);
            ret += s;
        }
    }

    return ret;
}


CString
Ext2SysInformation()
{
    ULONG   i = 0;
    CString s;
    PEXT2_VOLUME  chain = gVols;

    s = "\r\nDisk devices:\r\n";
    for (i=0; i < g_nDisks; i++) {
        s += Ext2DiskInformation(&gDisks[i]);
    }

    s += "\r\nCdrom/DVD devices:\r\n";

    for (i=0; i < g_nCdroms; i++) {
        s += Ext2CdromInformation(&gCdroms[i]);
    }

    while (chain) {
        s += Ext2VolumeInformation(chain);
        chain = chain->Next;
    }

    return s;
}

VOID
Ext2CleanupDisks()
{
    ULONG   i = 0;

    for (i=0; i < g_nDisks; i++) {
        if (gDisks[i].bLoaded) {
            if (gDisks[i].Layout) {
                free(gDisks[i].Layout);
                gDisks[i].Layout = NULL;
            }
            gDisks[i].bLoaded = FALSE;
            if (gDisks[i].DataParts) {
                free(gDisks[i].DataParts);
            }
        }
    }

    g_nDisks = 0;
    if (gDisks) {
        free(gDisks);
        gDisks = NULL;
    }
}

BOOLEAN
Ext2LoadCdroms()
{
    ULONG           i = 0, j = 0;
    NT::NTSTATUS    status;
    DWORD           mediaType;

    if (g_nCdroms == 0) {
        return TRUE;
    }

    gCdroms = (PEXT2_CDROM) malloc(sizeof(EXT2_CDROM) * g_nCdroms);
    if (gCdroms == NULL) {
        return FALSE;
    }
    memset(gCdroms, 0, sizeof(EXT2_CDROM) * g_nCdroms);

    while (i < g_nCdroms && j < 256) {

        HANDLE          Handle = NULL;

        gCdroms[i].Magic[0] = EXT2_CDROM_DEVICE_MAGIC;
        gCdroms[i].Magic[1] = EXT2_CDROM_VOLUME_MAGIC;
        gCdroms[i].OrderNo = (UCHAR) j;
        gCdroms[i].bLoaded = FALSE;
        sprintf(gCdroms[i].Name, "\\Device\\Cdrom%d\0", j);

        j++;

        status = Ext2Open(gCdroms[i].Name, &Handle, EXT2_DESIRED_ACCESS);
        if (!NT_SUCCESS(status)) {
            continue;
        }

        status = Ext2QueryProperty( Handle, StorageDeviceProperty,
                                    (PVOID)&gCdroms[i].SDD,
                                    sizeof(STORAGE_DEVICE_DESCRIPTOR)
                                  );
        if (!NT_SUCCESS(status)) {
            goto Next;
        }

        status = Ext2QueryProperty( Handle, StorageAdapterProperty,
                                    (PVOID)&gCdroms[i].SAD,
                                    sizeof(STORAGE_ADAPTER_DESCRIPTOR)
                                  );
        if (!NT_SUCCESS(status)) {
            goto Next;
        }

        status = Ext2QueryDisk(Handle, &gCdroms[i].DiskGeometry);
        if (NT_SUCCESS(status)) {
            gCdroms[i].bEjected = FALSE;
        } else {
            // (status == STATUS_NO_MEDIA_IN_DEVICE) {
            gCdroms[i].bEjected = TRUE;
        }

        status = Ext2QueryMediaType(Handle, &mediaType);
        if (NT_SUCCESS(status) && mediaType == FILE_DEVICE_DVD) {
            gCdroms[i].bIsDVD = TRUE;
        } else {
            gCdroms[i].bIsDVD = FALSE;
        }

        if (!gCdroms[i].bEjected) {
            Ext2QueryExt2Property(Handle, &gCdroms[i].EVP);
        }

        gCdroms[i].bLoaded = TRUE;

Next:

        Ext2Close(&Handle);
        i++;
    }

    g_nCdroms = i;
    return TRUE;
}

VOID
Ext2LoadCdromDrvLetters()
{
    for (ULONG i = 0; i < g_nCdroms; i++) {
        gCdroms[i].DrvLetters = Ext2QueryCdromDrvLetters(&gCdroms[i]);
    }
}


VOID
Ext2CleanupCdroms()
{
    ULONG   i = 0;

    for (i=0; i < g_nCdroms; i++) {
        if (gCdroms[i].bLoaded) {
            gCdroms[i].bLoaded = FALSE;
        }
    }

    g_nCdroms = 0;
    if (gCdroms) {
        free(gCdroms);
        gCdroms = NULL;
    }
}

BOOLEAN
Ext2CompareExtents(
    PVOLUME_DISK_EXTENTS ext1,
    PVOLUME_DISK_EXTENTS ext2
)
{
    DWORD nExt;

    if (ext1->NumberOfDiskExtents != ext2->NumberOfDiskExtents) {
        return FALSE;
    }

    for (nExt = 0; nExt < ext1->NumberOfDiskExtents; nExt++) {
        if ((ext1->Extents[nExt].DiskNumber != ext2->Extents[nExt].DiskNumber) ||
                (ext1->Extents[nExt].StartingOffset.QuadPart != ext2->Extents[nExt].StartingOffset.QuadPart) ||
                (ext1->Extents[nExt].ExtentLength.QuadPart != ext2->Extents[nExt].ExtentLength.QuadPart) ) {
            return FALSE;
        }
    }
    return TRUE;
}

ULONGLONG
Ext2EjectedDiskLetters(
    PEXT2_DISK   Disk
)
{
    ULONGLONG letters = 0;
    int i;

    /* checking the digits ltters */
    for (i=0; i < 10; i++) {
        if (drvDigits[i].bUsed && drvDigits[i].SDN) {
            if (drvDigits[i].SDN->DeviceNumber == Disk->OrderNo) {
                letters |= (((ULONGLONG) 1) << (32 + i));
            }
        }
    }

    for (i=0; i <26; i++) {
        if (drvLetters[i].bUsed && drvLetters[i].SDN) {
            if (drvLetters[i].SDN->DeviceNumber == Disk->OrderNo) {
                letters |= (((ULONGLONG) 1) << i);
            }
        }
    }

    return letters;
}

ULONGLONG
Ext2QueryVolumeDrvLetters(PEXT2_VOLUME Volume)
{
    ULONGLONG letters = 0;
    int i;

    UCHAR   drvChar = Ext2QueryMountPoint(Volume->Name);
    if (drvChar) {
        letters |= (((ULONGLONG) 1) << (drvChar - 'A'));
    }

    if (!Volume->Extent) {
        goto errorout;
    }

    /* checking the digits ltters */
    for (i=0; i < 10; i++) {
        if (drvDigits[i].bUsed && drvDigits[i].Extent) {
            if (Ext2CompareExtents(drvDigits[i].Extent, Volume->Extent)) {
                letters |= (((ULONGLONG) 1) << (32 + i));
                if (drvChar != drvDigits[i].Letter) {
                    drvDigits[i].bTemporary = TRUE;
                }
            }
        }
    }

    for (i=0; i <26; i++) {
        if (drvLetters[i].bUsed && drvLetters[i].Extent) {
            if (Ext2CompareExtents(drvLetters[i].Extent, Volume->Extent)) {
                letters |= (((ULONGLONG) 1) << i);
                if (drvChar != drvLetters[i].Letter) {
                    drvLetters[i].bTemporary = TRUE;
                }
            }
        }
    }

errorout:

    return letters;
}

ULONGLONG
Ext2QueryCdromDrvLetters(PEXT2_CDROM Cdrom)
{
    ULONGLONG letters = 0;
    UCHAR   drvChar;
    int i;

    if (!Cdrom) {
        goto errorout;
    }

    drvChar = Ext2QueryMountPoint(Cdrom->Name);
    if (drvChar) {
        letters |= (((ULONGLONG) 1) << (drvChar - 'A'));
    }

    /* checking the digits ltters */
    for (i=0; i < 10; i++) {
        if (drvDigits[i].bUsed && drvDigits[i].DrvType == DRIVE_CDROM) {
            if (!_stricmp(drvDigits[i].SymLink, Cdrom->Name)) {
                letters |= (((ULONGLONG) 1) << (i + 32));
            }
        }
    }

    for (i=0; i <26; i++) {
        if (drvLetters[i].bUsed &&  drvLetters[i].DrvType == DRIVE_CDROM) {
            if (!_stricmp(drvLetters[i].SymLink, Cdrom->Name)) {
                letters |= (((ULONGLONG) 1) << i);
            }
        }
    }

errorout:

    return letters;
}

BOOLEAN
Ext2QueryVolumeFS(
    HANDLE          Handle,
    PEXT2_VOLUME    volume
)
{
    struct ext2_super_block *sb = NULL;
    union swap_header*	swap = NULL;
    PUCHAR  buffer = NULL;


    NT::NTSTATUS          status;

    buffer = (PUCHAR)malloc(PAGE_SIZE);
    if (!buffer) {
        return FALSE;
    }

    swap = (union swap_header*)&buffer[SWAP_HEADER_OFFSET];
    sb = (struct ext2_super_block *)&buffer[SUPER_BLOCK_OFFSET];

    status = Ext2Read( Handle, FALSE, volume->FssInfo.BytesPerSector,
                       (ULONGLONG)0,PAGE_SIZE, (PUCHAR) buffer);

    if (!NT_SUCCESS(status)) {
        free(buffer);
        return FALSE;
    }

    if (sb->s_magic == EXT2_SUPER_MAGIC) {

        volume->FsaInfo.FileSystemNameLength = 8;
        volume->FsaInfo.FileSystemName[0] = (WCHAR)'E';
        volume->FsaInfo.FileSystemName[1] = (WCHAR)'X';
        volume->FsaInfo.FileSystemName[2] = (WCHAR)'T';
        volume->FsaInfo.FileSystemName[4] = 0;

        if ((sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) ||
                (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) ||
                (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) ) {
            volume->FsaInfo.FileSystemName[3] = (WCHAR)'3';
            volume->EVP.bExt3 = TRUE;
        } else {
            volume->FsaInfo.FileSystemName[3] = (WCHAR)'2';
            volume->EVP.bExt2 = TRUE;
        }
        memcpy(&volume->EVP.UUID[0], &sb->s_uuid[0], 16);
        goto errorout;
    }

    if ((memcmp(swap->magic.magic, SWAP_HEADER_MAGIC_V1, 10) == 0) ||
            (memcmp(swap->magic.magic, SWAP_HEADER_MAGIC_V2, 10) == 0)) {
        volume->FsaInfo.FileSystemNameLength = 8;
        volume->FsaInfo.FileSystemName[0] = (WCHAR)'S';
        volume->FsaInfo.FileSystemName[1] = (WCHAR)'W';
        volume->FsaInfo.FileSystemName[2] = (WCHAR)'A';
        volume->FsaInfo.FileSystemName[3] = (WCHAR)'P';
        volume->FsaInfo.FileSystemName[4] = 0;
    }

errorout:

    free(buffer);
    return (volume->EVP.bExt2 || volume->EVP.bExt3);
}

BOOLEAN
Ext2QueryExt2Property (
    HANDLE                      Handle,
    PEXT2_VOLUME_PROPERTY2      EVP
)
{
    NT::NTSTATUS                status;
    NT::IO_STATUS_BLOCK         iosb;

    BOOLEAN                     bExt2, bExt3;
    CHAR                        UUID[16];

    if (!Ext2IsServiceStarted()) {
        return FALSE;
    }

    bExt2 = EVP->bExt2;
    bExt3 = EVP->bExt3;
    memcpy(&UUID[0], &EVP->UUID[0], 16);
    memset(EVP, 0, sizeof(EXT2_VOLUME_PROPERTY2));
    memcpy(&EVP->UUID[0], &UUID[0], 16);
    EVP->bExt2 = bExt2;
    EVP->bExt3 = bExt3;
    EVP->Magic = EXT2_VOLUME_PROPERTY_MAGIC;
    EVP->Command = APP_CMD_QUERY_PROPERTY2;

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &iosb,
                 IOCTL_APP_VOLUME_PROPERTY,
                 EVP, sizeof(EXT2_VOLUME_PROPERTY2),
                 EVP, sizeof(EXT2_VOLUME_PROPERTY2)
             );

    return NT_SUCCESS(status);
}


BOOLEAN
Ext2QueryPerfStat (
    HANDLE                      Handle,
    PEXT2_QUERY_PERFSTAT        Stat,
    PEXT2_PERF_STATISTICS_V1   *PerfV1,
    PEXT2_PERF_STATISTICS_V2   *PerfV2
)
{
    NT::NTSTATUS                status;
    NT::IO_STATUS_BLOCK         iosb;

    memset(Stat, 0, sizeof(EXT2_QUERY_PERFSTAT));
    Stat->Magic = EXT2_VOLUME_PROPERTY_MAGIC;
    Stat->Command = IOCTL_APP_QUERY_PERFSTAT;

    *PerfV1 = NULL;
    *PerfV2 = NULL;

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &iosb,
                 IOCTL_APP_QUERY_PERFSTAT,
                 Stat, sizeof(EXT2_QUERY_PERFSTAT),
                 Stat, sizeof(EXT2_QUERY_PERFSTAT)
             );
    if (!NT_SUCCESS(!status))
        return FALSE;

    if (iosb.Information == EXT2_QUERY_PERFSTAT_SZV2 &&
            (Stat->Flags & EXT2_QUERY_PERFSTAT_VER2) != 0) {

        if (Stat->PerfStatV2.Magic == EXT2_PERF_STAT_MAGIC &&
                Stat->PerfStatV2.Length == sizeof(EXT2_PERF_STATISTICS_V2) &&
                Stat->PerfStatV2.Version == EXT2_PERF_STAT_VER2) {
            *PerfV2 = &Stat->PerfStatV2;
        }

    } else if (iosb.Information >= EXT2_QUERY_PERFSTAT_SZV1)  {

        *PerfV1 = &Stat->PerfStatV1;
    }

    if (PerfV1 || PerfV2)
        return TRUE;

    return FALSE;
}

VOID
Ext2StorePropertyinRegistry(PEXT2_VOLUME_PROPERTY2 EVP)
{
    CHAR    UUID[50];
    HKEY    hKey;
    CHAR    keyPath[MAX_PATH];
    LONG    status;
    CString data = "";

    int     i;
    int     len = 0;

    memset(UUID, 0, 50);
    for (i=0; i < 16; i++) {
        if (i == 0) {
            sprintf(&UUID[len], "{%2.2X", EVP->UUID[i]);
            len += 3;
        } else if (i == 15) {
            sprintf(&UUID[len], "-%2.2X}", EVP->UUID[i]);
            len +=4;
        } else {
            sprintf(&UUID[len], "-%2.2X", EVP->UUID[i]);
            len += 3;
        }
    }

    /* Create or open ext2fsd volumes key */
    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Services\\Ext2Fsd\\Volumes") ;
    status = ::RegCreateKeyEx (HKEY_LOCAL_MACHINE,
                               keyPath,
                               0,
                               0,
                               REG_OPTION_NON_VOLATILE,
                               KEY_ALL_ACCESS,
                               NULL,
                               &hKey,
                               NULL);
    if (status != ERROR_SUCCESS) {
        return;
    }

#define READING_ONLY        "Readonly"
#define WRITING_SUPPORT     "WritingSupport"
#define EXT3_FORCEWRITING   "Ext3ForceWriting"
#define CODEPAGE_NAME       "CodePage"
#define HIDING_PREFIX       "HidingPrefix"
#define HIDING_SUFFIX       "HidingSuffix"
#define MOUNT_POINT         "MountPoint"

    if (EVP->bReadonly) {
        data += READING_ONLY";";
    } else if (EVP->bExt3 && EVP->bExt3Writable) {
        data += EXT3_FORCEWRITING";";
    }

    if (EVP->DrvLetter) {
        if ((EVP->DrvLetter & 0x7F) == 0 || EVP->DrvLetter == 0xFF) {
            data += MOUNT_POINT";";
        } else {
            data += MOUNT_POINT"=";
            data += (CHAR)(EVP->DrvLetter & 0x7F);
            data += ":;";
        }
    }

    if (strlen((CHAR*)EVP->Codepage) > 0) {
        data += CODEPAGE_NAME"=";
        data += &EVP->Codepage[0];
        data += ";";
    }

    if (EVP->bHidingPrefix) {
        data += HIDING_PREFIX"=";
        data += EVP->sHidingPrefix;
        data += ";";
    }

    if (EVP->bHidingSuffix) {
        data += HIDING_SUFFIX"=";
        data += EVP->sHidingSuffix;
        data += ";";
    }

    /* set volume parameters */
    status = RegSetValueEx( hKey, UUID, 0, REG_SZ, (BYTE *)
                            data.GetBuffer(data.GetLength()),
                            data.GetLength());

    RegCloseKey(hKey);
}

BOOLEAN Ext2IsNullUuid (__u8 * uuid)
{
    int i;
    for (i = 0; i < 16; i++) {
        if (uuid[i]) {
            break;
        }
    }

    return (i >= 16);
}

BOOLEAN
Ext2CheckVolumeRegistryProperty(PEXT2_VOLUME_PROPERTY2 EVP)
{
    CHAR    UUID[50];
    HKEY    hKey;
    CHAR    keyPath[MAX_PATH];
    CHAR    content[MAX_PATH];
    LONG    status, type = 0;

    int     i;
    int     len = 0;
    BOOLEAN rc = TRUE;

    if (Ext2IsNullUuid(&EVP->UUID[0])) {
        return TRUE;
    }

    memset(UUID, 0, 50);
    for (i=0; i < 16; i++) {
        if (i == 0) {
            sprintf(&UUID[len], "{%2.2X", EVP->UUID[i]);
            len += 3;
        } else if (i == 15) {
            sprintf(&UUID[len], "-%2.2X}", EVP->UUID[i]);
            len +=4;
        } else {
            sprintf(&UUID[len], "-%2.2X", EVP->UUID[i]);
            len += 3;
        }
    }

    /* Create or open ext2fsd volumes key */
    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Services\\Ext2Fsd\\Volumes") ;
    status = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                             keyPath,
                             0,
                             KEY_ALL_ACCESS,
                             &hKey) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    /* Query volume parameters */
    len = MAX_PATH;
    status = RegQueryValueEx(hKey,
                             &UUID[0],
                             0,
                             (LPDWORD)&type,
                             (BYTE *)&content[0],
                             (LPDWORD)&len);
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
    }

    RegCloseKey(hKey);

errorout:
    return rc;
}

VOID
Ext2SetDefaultVolumeRegistryProperty(PEXT2_VOLUME_PROPERTY2 EVP)
{
    ULONG   StartMode;
    BOOLEAN AutoMount = 0;

    if (Ext2IsNullUuid(&EVP->UUID[0])) {
        return;
    }

    /* query global parameters */
    Ext2QueryGlobalProperty(
        &StartMode,
        (BOOLEAN *)&EVP->bReadonly,
        (BOOLEAN *)&EVP->bExt3Writable,
        (CHAR *)EVP->Codepage,
        (CHAR *)NULL,
        (CHAR *)NULL,
        (BOOLEAN *)&AutoMount
    );

    if (EVP->bExt3 && !EVP->bExt3Writable)
        EVP->bReadonly = TRUE;

    EVP->DrvLetter = 0x80;

    Ext2StorePropertyinRegistry(EVP);
}

BOOLEAN
Ext2SetExt2Property (
    HANDLE                Handle,
    PEXT2_VOLUME_PROPERTY2 EVP
)
{
    NT::NTSTATUS                status;
    NT::IO_STATUS_BLOCK         iosb;

    ASSERT(EVP->Magic == EXT2_VOLUME_PROPERTY_MAGIC);
    EVP->Command = APP_CMD_SET_PROPERTY2;

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &iosb,
                 IOCTL_APP_VOLUME_PROPERTY,
                 EVP, sizeof(EXT2_VOLUME_PROPERTY2),
                 EVP, sizeof(EXT2_VOLUME_PROPERTY2)
             );

    if (NT_SUCCESS(status)) {
        return TRUE;
    } else {
        CString s;
        s.Format("Status = %xh\n", status);
        AfxMessageBox(s);
    }

    return FALSE;
}

BOOLEAN
Ext2SetExt2Property3 (
    HANDLE                Handle,
    PEXT2_VOLUME_PROPERTY3 EVP
)
{
    NT::NTSTATUS                status;
    NT::IO_STATUS_BLOCK         iosb;

    ASSERT(EVP->Prop2.Magic == EXT2_VOLUME_PROPERTY_MAGIC);
    EVP->Prop2.Command = APP_CMD_SET_PROPERTY3;

    status = NT::ZwDeviceIoControlFile(
                 Handle, NULL, NULL, NULL, &iosb,
                 IOCTL_APP_VOLUME_PROPERTY,
                 EVP, sizeof(EXT2_VOLUME_PROPERTY3),
                 EVP, sizeof(EXT2_VOLUME_PROPERTY3)
             );

    if (NT_SUCCESS(status)) {
        return TRUE;
    } else {
        CString s;
        s.Format("Status = %xh\n", status);
        AfxMessageBox(s);
    }

    return FALSE;
}


BOOLEAN
Ext2QueryGlobalProperty(
    ULONG *     ulStartup,
    BOOLEAN *   bReadonly,
    BOOLEAN *   bExt3Writable,
    CHAR *      Codepage,
    CHAR *      sPrefix,
    CHAR *      sSuffix,
    BOOLEAN *   bAutoMount
)
{
    int     rc = TRUE;
    HKEY    hKey;
    CHAR    keyPath[MAX_PATH];
    DWORD   data = 0;
    LONG    status, type, len;

    /* Open ext2fsd sevice key */
    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Services\\Ext2Fsd") ;
    status = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                             keyPath,
                             0,
                             KEY_ALL_ACCESS,
                             &hKey) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    /* Query Start type */
    len = sizeof(DWORD);
    status = RegQueryValueEx( hKey,
                              "Start",
                              0,
                              (LPDWORD)&type,
                              (BYTE *)&data,
                              (LPDWORD)&len);
    if (status == ERROR_SUCCESS) {
        *ulStartup = data;
    }

    RegCloseKey(hKey);

    /* Open ext2fsd parameters key */
    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Services\\Ext2Fsd\\Parameters") ;
    status = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                             keyPath,
                             0,
                             KEY_ALL_ACCESS,
                             &hKey) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    /* Query WritingSupport */
    len = sizeof(DWORD);
    status = RegQueryValueEx( hKey,
                              "WritingSupport",
                              0,
                              (LPDWORD)&type,
                              (BYTE *)&data,
                              (LPDWORD)&len);
    if (status == ERROR_SUCCESS) {
        *bReadonly = (data == 0);
    }

    /* query Ext3ForceWriting */
    len = sizeof(DWORD);
    status = RegQueryValueEx( hKey,
                              "Ext3ForceWriting",
                              0,
                              (LPDWORD)&type,
                              (BYTE *)&data,
                              (LPDWORD)&len);

    if (status == ERROR_SUCCESS) {
        *bExt3Writable = (data != 0);
    }

    /* query AutoMount */
    len = sizeof(DWORD);
    status = RegQueryValueEx( hKey,
                              "AutoMount",
                              0,
                              (LPDWORD)&type,
                              (BYTE *)&data,
                              (LPDWORD)&len);

    if (status == ERROR_SUCCESS) {
        *bAutoMount = (data != 0);
    }

    if (Codepage) {
        /* query codepage */
        len = CODEPAGE_MAXLEN;
        status = RegQueryValueEx( hKey,
                                  "CodePage",
                                  0,
                                  (LPDWORD)&type,
                                  (BYTE *)Codepage,
                                  (LPDWORD)&len);
    }

    if (sPrefix) {
        /* querying hidding filter patterns */
        len = CODEPAGE_MAXLEN;
        status = RegQueryValueEx( hKey,
                                  "HidingPrefix",
                                  0,
                                  (LPDWORD)&type,
                                  (BYTE *)sPrefix,
                                  (LPDWORD)&len);
    }

    if (sSuffix) {
        len = CODEPAGE_MAXLEN;
        status = RegQueryValueEx( hKey,
                                  "HidingSuffix",
                                  0,
                                  (LPDWORD)&type,
                                  (BYTE *)sSuffix,
                                  (LPDWORD)&len);
    }

    RegCloseKey(hKey);


errorout:

    return rc;
}

INT
Ext2QueryDrvVersion(
    CHAR *      Version,
    CHAR *      Date,
    CHAR *      Time
)
{
    EXT2_VOLUME_PROPERTY_VERSION EVPV;
    NT::NTSTATUS                 status;
    HANDLE                       handle = NULL;
    NT::IO_STATUS_BLOCK          iosb;
    INT                          rc = 0;

    memset(&EVPV, 0, sizeof(EXT2_VOLUME_PROPERTY_VERSION));
    EVPV.Magic = EXT2_VOLUME_PROPERTY_MAGIC;
    EVPV.Command = APP_CMD_QUERY_VERSION;
    EVPV.Flags |= EXT2_FLAG_VP_SET_GLOBAL;

    status = Ext2Open("\\DosDevices\\Ext2Fsd",
                      &handle, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        rc = -1;
        goto errorout;
    }

    status = NT::ZwDeviceIoControlFile(
                 handle, NULL, NULL, NULL, &iosb,
                 IOCTL_APP_VOLUME_PROPERTY,
                 &EVPV, sizeof(EXT2_VOLUME_PROPERTY_VERSION),
                 &EVPV, sizeof(EXT2_VOLUME_PROPERTY_VERSION)
             );

    if (NT_SUCCESS(status)) {
        strncpy(Version,  EVPV.Version, 0x1B);
        strncpy(Date,     EVPV.Date,    0x1F);
        strncpy(Time,     EVPV.Time,    0x1F);
    }

    rc = NT_SUCCESS(status);

errorout:

    Ext2Close(&handle);

    return rc;
}

BOOLEAN
Ext2SetGlobalProperty (
    ULONG       ulStartup,
    BOOLEAN     bReadonly,
    BOOLEAN     bExt3Writable,
    CHAR *      Codepage,
    CHAR *      sPrefix,
    CHAR *      sSuffix,
    BOOLEAN     bAutoMount
)
{
    EXT2_VOLUME_PROPERTY2 EVP;
    EXT2_VOLUME_PROPERTY3 EVP3;

    NT::NTSTATUS         status;
    HANDLE  Handle = NULL;
    int     rc = TRUE;
    HKEY    hKey;
    CHAR    keyPath[MAX_PATH] ;

    ULONG   data = 0;

    memset(&EVP, 0, sizeof(EXT2_VOLUME_PROPERTY2));
    EVP.Magic = EXT2_VOLUME_PROPERTY_MAGIC;
    EVP.Command = APP_CMD_SET_PROPERTY2;
    EVP.Flags |= EXT2_FLAG_VP_SET_GLOBAL;
    EVP.bReadonly = bReadonly;
    EVP.bExt3Writable = bExt3Writable;
    strcpy((CHAR *)EVP.Codepage, Codepage);

    if (strlen(sPrefix)) {
        strcpy(EVP.sHidingPrefix, sPrefix);
        EVP.bHidingPrefix = TRUE;
    }

    if (strlen(sSuffix)) {
        strcpy(EVP.sHidingSuffix, sSuffix);
        EVP.bHidingSuffix = TRUE;
    }

    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Services\\Ext2Fsd") ;
    status = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                             keyPath,
                             0,
                             KEY_ALL_ACCESS,
                             &hKey) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    /* set Start type */
    status = RegSetValueEx( hKey,
                            "Start",
                            0,
                            REG_DWORD,
                            (BYTE *)&ulStartup,
                            sizeof(DWORD));
    RegCloseKey(hKey);

    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Services\\Ext2Fsd\\Parameters") ;
    status = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                             keyPath,
                             0,
                             KEY_ALL_ACCESS,
                             &hKey) ;
    if (status != ERROR_SUCCESS) {
        rc = FALSE;
        goto errorout;
    }

    /* set WritingSupport */
    data = !bReadonly;
    status = RegSetValueEx( hKey,
                            "WritingSupport",
                            0,
                            REG_DWORD,
                            (BYTE *)&data,
                            sizeof(ULONG));

    /* set Ext3ForceWriting */
    data = bExt3Writable;
    status = RegSetValueEx( hKey,
                            "Ext3ForceWriting",
                            0,
                            REG_DWORD,
                            (BYTE *)&data,
                            sizeof(ULONG));
    /* set AutoMount */
    data = bAutoMount;
    status = RegSetValueEx( hKey,
                            "AutoMount",
                            0,
                            REG_DWORD,
                            (BYTE *)&data,
                            sizeof(ULONG));


    /* set codepage */
    status = RegSetValueEx( hKey,
                            "CodePage",
                            0,
                            REG_SZ,
                            (BYTE *)Codepage,
                            strlen(Codepage));

    /* set hiding filter patterns */
    status = RegSetValueEx( hKey,
                            "HidingPrefix",
                            0,
                            REG_SZ,
                            (BYTE *)sPrefix,
                            strlen(sPrefix));

    status = RegSetValueEx( hKey,
                            "HidingSuffix",
                            0,
                            REG_SZ,
                            (BYTE *)sSuffix,
                            strlen(sSuffix));

    RegCloseKey(hKey);

    status = Ext2Open("\\DosDevices\\Ext2Fsd", &Handle, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    memset(&EVP3, 0, sizeof(EXT2_VOLUME_PROPERTY3));
    EVP3.Prop2 = EVP;
    EVP3.Flags = EXT2_VPROP3_AUTOMOUNT;
    EVP3.AutoMount = g_bAutoMount;

    rc = Ext2SetExt2Property3(Handle, &EVP3);
    if (!rc) {
        rc = Ext2SetExt2Property(Handle, &EVP);
        if (!rc) {
            goto errorout;
        }
    }

errorout:

    Ext2Close(&Handle);

    return rc;
}

BOOLEAN
Ext2IsServiceStarted()
{
    NT::NTSTATUS         status;
    HANDLE               Handle = NULL;

    status = Ext2Open("\\DosDevices\\Ext2Fsd",
                      &Handle, EXT2_DESIRED_ACCESS);

    if (NT_SUCCESS(status)) {
        Ext2Close(&Handle);
        return TRUE;
    }

    return FALSE;
}

BOOLEAN
Ext2StartService()
{
    BOOLEAN     rc = FALSE;
    SC_HANDLE   scmHandle = NULL;
    SC_HANDLE   drvHandle = NULL;

    if (Ext2IsServiceStarted()) {
        return TRUE;
    }

    scmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scmHandle) {
        goto errorout;
    }

    drvHandle = OpenService(scmHandle,
                            "ext2fsd",
                            SERVICE_ALL_ACCESS);
    if (!drvHandle) {
        goto errorout;
    }

    rc = (0 != StartService(drvHandle, 0, NULL));

errorout:

    if (drvHandle)
        CloseServiceHandle(drvHandle);

    if (scmHandle) {
        CloseServiceHandle(scmHandle);
    }

    return rc;
}

BOOLEAN
Ext2IsRemovable(PEXT2_VOLUME volume)
{
    STORAGE_BUS_TYPE busType = BusTypeAta;
    BOOLEAN bRemovableMedia = FALSE;

    if (volume && volume->Part) {
        busType = volume->Part->Disk->SDD.BusType;
        bRemovableMedia = volume->Part->Disk->SDD.RemovableMedia;
    }

    if (busType == BusType1394 ||
            busType == BusTypeUsb ||
            bRemovableMedia  ) {
        return TRUE;
    }

    return FALSE;
}

BOOLEAN
Ext2InitializeVolume(
    PCHAR           NameString,
    PEXT2_VOLUME *  Volume
)
{
    BOOLEAN rc = FALSE;
    HANDLE hVolume = NULL;
    NT::NTSTATUS status;
    NT::IO_STATUS_BLOCK ioSb;
    PEXT2_VOLUME        volume = NULL;

    volume = (PEXT2_VOLUME) malloc(sizeof(EXT2_VOLUME));
    if (!volume) {
        goto errorout;
    }

    status = Ext2Open(NameString, &hVolume, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        free(volume);
        volume = NULL;
        goto errorout;
    }

    memset(volume, 0, sizeof(EXT2_VOLUME));
    volume->Magic = EXT2_VOLUME_MAGIC;
    strcpy(volume->Name, NameString);

    status = NT::ZwQueryVolumeInformationFile(
                 hVolume, &ioSb, &volume->FsaInfo,
                 MAX_PATH, NT::FileFsAttributeInformation
             );

    if (NT_SUCCESS(status)) {

        NT::UNICODE_STRING uniString;
        NT::ANSI_STRING    aniString;

        NT::ZwQueryVolumeInformationFile(
            hVolume, &ioSb, &volume->FsdInfo,
            sizeof(NT::FILE_FS_DEVICE_INFORMATION),
            NT::FileFsDeviceInformation
        );

        NT::ZwQueryVolumeInformationFile(
            hVolume, &ioSb, &volume->FssInfo,
            sizeof(NT::FILE_FS_SIZE_INFORMATION),
            NT::FileFsSizeInformation
        );

        if (volume->FsaInfo.FileSystemNameLength == 6 &&
                (volume->FsaInfo.FileSystemName[0] == (WCHAR)'R' ||
                 volume->FsaInfo.FileSystemName[0] == (WCHAR)'r') &&
                (volume->FsaInfo.FileSystemName[1] == (WCHAR)'A' ||
                 volume->FsaInfo.FileSystemName[1] == (WCHAR)'a') &&
                (volume->FsaInfo.FileSystemName[2] == (WCHAR)'W' ||
                 volume->FsaInfo.FileSystemName[2] == (WCHAR)'w')) {
            if (!Ext2QueryVolumeFS(hVolume, volume)) {
                /* memcpy(volume->Codepage, "N/A", 3); */
            }

        } else {
            if (Ext2QueryExt2Property(hVolume, &volume->EVP) &&
                    volume->EVP.DrvLetter != 0xFF &&
                    volume->EVP.DrvLetter != 0 ) {
                if (Ext2NotifyVolumePoint(volume, volume->EVP.DrvLetter)) {
                    volume->EVP.DrvLetter = 0xFF;
                    Ext2SetExt2Property(hVolume, &volume->EVP);
                }
            }
            volume->bRecognized = TRUE;
        }

        /* convert the unicode file system name to mbs */

        uniString.MaximumLength = uniString.Length =
                                      (USHORT)volume->FsaInfo.FileSystemNameLength;
        uniString.Buffer = volume->FsaInfo.FileSystemName;

        aniString.Buffer = volume->FileSystem;
        aniString.Length = 0;
        aniString.MaximumLength = 64;

        memset(volume->FileSystem, 0, 64);
        NT::RtlUnicodeStringToAnsiString(&aniString, &uniString, FALSE);
    }

    volume->Extent = Ext2QueryVolumeExtents(hVolume);
    if (!volume->Extent) {
        free(volume);
        volume = NULL;
        goto errorout;
    }

    *Volume = volume;
    rc = TRUE;

errorout:

    Ext2Close(&hVolume);
    return rc;
}

BOOLEAN
Ext2IsPartitionExtent(
    ULONG                  disk,
    PPARTITION_INFORMATION_EXT part,
    PVOLUME_DISK_EXTENTS   extent
)
{
    DWORD nExt = 0;

    for (nExt = 0; nExt < extent->NumberOfDiskExtents; nExt++) {
        if ((extent->Extents[nExt].DiskNumber == disk) &&
                (extent->Extents[nExt].StartingOffset.QuadPart ==
                 part->StartingOffset.QuadPart ) &&
                (extent->Extents[nExt].ExtentLength.QuadPart ==
                 part->PartitionLength.QuadPart)) {
            return TRUE;
        }
    }
    return FALSE;
}

/* should be called after volumes' initiaization */
BOOLEAN
Ext2LoadDiskPartitions(PEXT2_DISK Disk)
{
    UCHAR i = 0, cnt = 0;
    ULONG j = 0;

    BOOLEAN bDynamic = FALSE;

    if (Disk->NumParts == 0) {
        Disk->NumParts = 1;
    }

    Disk->DataParts = (PEXT2_PARTITION) malloc(
                          sizeof(EXT2_PARTITION) * Disk->NumParts);
    if (!Disk->DataParts) {
        return FALSE;
    }

    memset(Disk->DataParts, 0, sizeof(EXT2_PARTITION) * Disk->NumParts);

    if (Disk->Layout) {
        if (Disk->Layout->PartitionStyle == PARTITION_STYLE_MBR &&
                Disk->Layout->PartitionEntry->Mbr.PartitionType == PARTITION_LDM) {
            bDynamic = TRUE;
        }


        /* Now walk through driveLayout and pack partitions */
        for (i = 0; (Disk->Layout != NULL) &&
                (i < (UCHAR)Disk->Layout->PartitionCount); i++) {

            PPARTITION_INFORMATION_EXT  Part;
            Part = &Disk->Layout->PartitionEntry[i];

            if (Disk->Layout->PartitionStyle == PARTITION_STYLE_MBR) {
                if (Part->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ||
                        Part->Mbr.PartitionType == PARTITION_EXTENDED ||
                        Part->Mbr.PartitionType == PARTITION_XINT13_EXTENDED) {
                    continue;
                }
            }

            sprintf(&Disk->DataParts[cnt].Name[0],
                    "\\Device\\Harddisk%u\\Partition%u",
                    Disk->OrderNo, cnt + 1);
            Disk->DataParts[cnt].Magic = EXT2_PART_MAGIC;
            Disk->DataParts[cnt].Disk = Disk;
            Disk->DataParts[cnt].Number = cnt + 1;
            Disk->DataParts[cnt++].Entry = Part;
        }

        /* Search the volumes of the partition */
        for (i=0; i < cnt; i++) {

            PEXT2_VOLUME   volume = gVols;
            for (j=0; (ULONG)j < g_nVols; j++) {
                if (Ext2IsPartitionExtent(Disk->OrderNo,
                                          Disk->DataParts[i].Entry,
                                          volume->Extent )) {
                    volume->bDynamic = bDynamic;
                    if (volume->Extent->NumberOfDiskExtents == 1) {
                        volume->Part = &Disk->DataParts[i];
                    }
                    Disk->DataParts[i].Volume = volume;
                    Disk->DataParts[i].DrvLetters = volume->DrvLetters;
                    break;
                }
                volume = volume->Next;
            }
        }
    } else {
        Disk->DataParts[cnt].Magic = EXT2_PART_MAGIC;
        Disk->DataParts[cnt].Disk = Disk;
        Disk->DataParts[cnt].Number = cnt + 1;
        Disk->DataParts[cnt].DrvLetters = Ext2EjectedDiskLetters(Disk);
    }

    return TRUE;
}


VOID
Ext2LoadAllDiskPartitions()
{
    for (ULONG i=0; i < g_nDisks; i++) {
        Ext2LoadDiskPartitions(&gDisks[i]);
    }
}

VOID
Ext2MountingVolumes()
{
    PEXT2_VOLUME   volume = gVols;
    int j;

    if (!Ext2IsServiceStarted()) {
        return;
    }

    for (j=0; (ULONG)j < g_nVols; j++) {
        if ((volume->EVP.bExt2 || volume->EVP.bExt3) && !volume->bRecognized) {
            if (Ext2IsRemovable(volume) || (volume->DrvLetters != 0) || g_bAutoMount) {
                if (!Ext2CheckVolumeRegistryProperty(&volume->EVP)) {
                    Ext2SetDefaultVolumeRegistryProperty(&volume->EVP);
                }
                Ext2NotifyVolumePoint(volume, 0);
            }
        }

        volume = volume->Next;
    }

}

/*
 * Must initialize driver letter before loading volumes
 */

BOOLEAN
Ext2LoadVolumes()
{
    BOOLEAN  rc = TRUE;

    TCHAR * nameString = NULL;
    ULONG   nameLen = REGSTR_VAL_MAX_HCID_LEN;

    HMACHINE hMachine = NULL;
    DEVNODE  dnRoot, dnFirst;

    DEVINSTID  devIds[] = {
        "ROOT\\FTDISK\\0000",
        "ROOT\\DMIO\\0000",
        NULL,
        "ROOT\\VOLMGR\\0000",
        NULL
    };

    int i = 0;

    if (IsVista()) {
        i = 3;
    }

    nameString = new TCHAR [nameLen];
    if (nameString == NULL) {
        goto errorout;
    }

    while (devIds[i] != NULL) {

        int n = 0;

        rc = CM_Locate_DevNode_Ex(&dnRoot, devIds[i],
                                  CM_LOCATE_DEVNODE_NORMAL , hMachine);
        if (rc != CR_SUCCESS) {
            break;
        }

        rc = CM_Get_Child_Ex(&dnFirst, dnRoot, 0, hMachine);
        if (rc != CR_SUCCESS) {
            goto errorout;
        }

        while (TRUE) {

            PEXT2_VOLUME volume = NULL;

            nameLen = REGSTR_VAL_MAX_HCID_LEN;
            memset(nameString, 0, nameLen);
            rc = CM_Get_DevNode_Registry_Property_Ex(
                     dnFirst, CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME,
                     NULL, nameString, &nameLen, 0, hMachine);
            if (rc != CR_SUCCESS) {
                break;
            }

            if (Ext2InitializeVolume(nameString, &volume)) {

                volume->bDynamic = 0;

                /* attach the volume to global list */
                if (gVols) {
                    PEXT2_VOLUME chain = gVols;
                    while (chain->Next) {
                        chain = chain->Next;
                    }
                    chain->Next = volume;
                } else {
                    gVols = volume;
                }
                g_nVols++;
            }

            rc = CM_Get_Sibling_Ex(&dnFirst, dnFirst, 0, hMachine);
            if (rc != CR_SUCCESS) {
                break;
            }
        }

        i++;
    }

errorout:

    if (nameString) {
        delete []nameString;
    }

    return TRUE;
}

BOOLEAN
Ext2LoadRemovableVolumes()
{
    BOOLEAN                     rc = FALSE;
    HDEVINFO                    devInfo = INVALID_HANDLE_VALUE;
    PSP_DEVICE_INTERFACE_DETAIL_DATA ifDetail = NULL;
    SP_DEVICE_INTERFACE_DATA    ifData;

    PCHAR                       nameString;
    int                         nInterface = 0;

    if (IsVista()) {
        return TRUE;
    }

    nameString = (PCHAR)malloc(REGSTR_VAL_MAX_HCID_LEN);
    if (nameString == NULL) {
        goto errorout;
    }

    ifDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(PAGE_SIZE);
    if (ifDetail == NULL) {
        goto errorout;
    }

    devInfo = SetupDiGetClassDevs(
                  &VolumeClassGuid, NULL, NULL,
                  DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
              );
    if (devInfo == INVALID_HANDLE_VALUE) {
        goto errorout;
    }

    while (TRUE) {

        BOOLEAN bFound = FALSE;

        ifData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        bFound = SetupDiEnumDeviceInterfaces(
                     devInfo,
                     NULL,
                     &VolumeClassGuid,
                     (ULONG)nInterface++,
                     &ifData);
        if (!bFound) {
            break;
        }

        memset(ifDetail, 0, PAGE_SIZE);
        ifDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
        if (SetupDiGetInterfaceDeviceDetail(
                    devInfo,
                    &ifData,
                    ifDetail,
                    PAGE_SIZE,
                    NULL,
                    NULL)) {

            PEXT2_VOLUME volume = NULL;

            memset(nameString, 0, REGSTR_VAL_MAX_HCID_LEN);
            QueryDosDevice(&ifDetail->DevicePath[4], nameString, REGSTR_VAL_MAX_HCID_LEN);

            if (Ext2InitializeVolume(nameString, &volume)) {

                if (volume->FsdInfo.Characteristics & FILE_REMOVABLE_MEDIA) {

                    /* attach the volume to global list */
                    if (gVols) {
                        PEXT2_VOLUME chain = gVols;
                        while (chain->Next) {
                            chain = chain->Next;
                        }
                        chain->Next = volume;
                    } else {
                        gVols = volume;
                    }
                    g_nVols++;
                } else {
                    if (volume->Extent) {
                        free(volume->Extent);
                    }
                    free(volume);
                }
            }
        }
    }

    rc = TRUE;

errorout:

    if (nameString) {
        free(nameString);
    }

    if (ifDetail) {
        free(ifDetail);
    }

    if (devInfo != INVALID_HANDLE_VALUE) {
        SetupDiDestroyDeviceInfoList(devInfo);
    }

    return rc;
}

VOID
Ext2LoadAllVolumeDrvLetters()
{
    PEXT2_VOLUME volume = gVols;
    BOOLEAN      started = Ext2IsServiceStarted();

    Ext2DrvLetters[0] = Ext2DrvLetters[1];
    Ext2DrvLetters[1] = 0;

    while (volume) {
        volume->DrvLetters = Ext2QueryVolumeDrvLetters(volume);
        if ( started && volume->bRecognized &&
                (volume->EVP.bExt2 || volume->EVP.bExt3)) {
            Ext2DrvLetters[1] |=  volume->DrvLetters;
        }
        volume = volume->Next;
    }
}


CString
Ext2QueryVolumeLetterStrings(
    ULONGLONG       letters,
    PEXT2_LETTER *  first
)
{
    CHAR        drvName[] = "C:\0";
    CString     str;
    int         i = 0;
    BOOLEAN     bInserted = FALSE;
    ULONGLONG   drive = 0;

    str.Empty();

    for (i=0; i < 10; i++) {
        drive = ((ULONGLONG) 1) << (i + 32);
        if (letters & drive) {
            if (bInserted) {
                str += ",";
            } else {
                str = "(";
                if (first) {
                    *first = &drvDigits[i];
                }
            }
            drvName[0] = '0' + i;
            str += drvName;
            bInserted = TRUE;
        }
    }

    for (i=0; i < 26; i++) {
        drive = ((ULONGLONG) 1) << (i);
        if (letters & drive) {
            if (bInserted) {
                str += ",";
            } else {
                str = "(";
                if (first) {
                    *first = &drvLetters[i];
                }
            }

            drvName[0] = 'A' + i;
            str += drvName;
            bInserted = TRUE;
        }
    }

    if (bInserted) {
        str += ")";
    }

    return str;
}

VOID
Ext2RefreshVLVI(
    CListCtrl *List,
    PEXT2_VOLUME chain,
    int  nItem
)
{
    ULONGLONG   totalSize, usedSize;
    CString     sSize, sUnit, s;

    PEXT2_LETTER  first = NULL;
    CString  Letters = Ext2QueryVolumeLetterStrings(
                           chain->DrvLetters, &first);

    List->SetItem( nItem, 1, LVIF_TEXT, (LPCSTR)Letters, 0, 0, 0,0);

    if (chain->bDynamic) {
        s.LoadString(IDS_DISK_TYPE_DYN);
    } else {
        s.LoadString(IDS_DISK_TYPE_BASIC);
    }
    List->SetItem(nItem, 2, LVIF_TEXT, (LPCSTR)s, 0, 0, 0,0);
    List->SetItem(nItem, 3, LVIF_TEXT, (LPCSTR)chain->FileSystem, 0, 0, 0,0);

    totalSize = chain->FssInfo.TotalAllocationUnits.QuadPart;
    usedSize  = totalSize - chain->FssInfo.AvailableAllocationUnits.QuadPart;
    totalSize = totalSize * chain->FssInfo.BytesPerSector *
                chain->FssInfo.SectorsPerAllocationUnit;
    usedSize  = usedSize * chain->FssInfo.BytesPerSector *
                chain->FssInfo.SectorsPerAllocationUnit;

    if (totalSize > (ULONGLONG) 10 * 1024 * 1024 * 1024) {
        totalSize = totalSize / ((ULONG)1024 * 1024 * 1024);
        usedSize  = usedSize / ((ULONG)1024 * 1024 * 1024);
        sUnit = " GB";
    } else if (totalSize > 10 * 1024 * 1024) {
        totalSize = totalSize / (1024 * 1024);
        usedSize  = usedSize / (1024 * 1024);
        sUnit = " MB";
    } else if (totalSize > 10 * 1024) {
        totalSize = totalSize / (1024);
        usedSize  = usedSize / (1024);
        sUnit = " KB";
    } else {
        sUnit = " B";
    }

    sSize.Format("%I64u", totalSize);
    sSize += sUnit;
    List->SetItem(nItem, 4, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    if (chain->bRecognized) {
        sSize.Format("%I64u", usedSize);
        sSize += sUnit;
    }
    List->SetItem(nItem, 5, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    List->SetItem(nItem, 6, LVIF_TEXT, (LPCSTR)chain->EVP.Codepage, 0, 0, 0,0);
    List->SetItem(nItem, 7, LVIF_TEXT, (LPCSTR)chain->Name, 0, 0, 0,0);

    /*
            List->SetItemState( nItem, LVIS_SELECTED | LVIS_FOCUSED ,
                                       LVIS_SELECTED | LVIS_FOCUSED);
    */
}


VOID
Ext2InsertVolume(
    CListCtrl *List,
    PEXT2_VOLUME chain
)
{
    int nItem = 0, nImage = 0;

    nItem = List->GetItemCount();
    nImage = 0;

    if (chain->FsdInfo.Characteristics & FILE_VIRTUAL_VOLUME) {
        nImage = IDI_DYNAMIC - IDI_FLOPPY;
    } else if (chain->FsdInfo.Characteristics & FILE_REMOVABLE_MEDIA) {
        nImage = IDI_FLOPPY - IDI_FLOPPY;
    } else {
        nImage = IDI_DISK - IDI_FLOPPY;
    }

    nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                              0, 0, nImage, (LONG)chain);

    Ext2RefreshVLVI(List, chain, nItem);
}

VOID
Ext2RefreshVLCD(
    CListCtrl *List,
    PEXT2_CDROM Cdrom,
    int nItem
)
{
    ULONGLONG   totalSize;
    CString     sSize, sUnit, s;

    PEXT2_LETTER  first = NULL;

    CString  Letters = Ext2QueryVolumeLetterStrings(
                           Cdrom->DrvLetters, &first);

    List->SetItem( nItem, 1, LVIF_TEXT, (LPCSTR)Letters, 0, 0, 0,0);

    s.LoadString(IDS_DISK_TYPE_BASIC);
    List->SetItem(nItem, 2, LVIF_TEXT, (LPCSTR)s, 0, 0, 0,0);
    List->SetItem(nItem, 3, LVIF_TEXT, (LPCSTR)"CDFS", 0, 0, 0,0);


    totalSize = (ULONGLONG)Cdrom->DiskGeometry.Cylinders.QuadPart;
    totalSize = totalSize * Cdrom->DiskGeometry.TracksPerCylinder *
                Cdrom->DiskGeometry.SectorsPerTrack *
                Cdrom->DiskGeometry.BytesPerSector;

    if (totalSize > 1024 * 1024 * 1024) {
        totalSize = totalSize / (1024 * 1024 * 1024);
        sUnit = " GB";
    } else if (totalSize > 1024 * 1024) {
        totalSize = totalSize / (1024 * 1024);
        sUnit = " MB";
    } else if (totalSize > 1024) {
        totalSize = totalSize / (1024);
        sUnit = " KB";
    } else {
        sUnit = " B";
    }

    sSize.Format("%I64u", totalSize);
    sSize += sUnit;
    List->SetItem(nItem, 4, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    sSize.Format("%I64u", totalSize);
    sSize += sUnit;
    List->SetItem(nItem, 5, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    List->SetItem(nItem, 8, LVIF_TEXT, (LPCSTR)Cdrom->Name, 0, 0, 0,0);
}

VOID
Ext2InsertCdromAsVolume(
    CListCtrl *List,
    PEXT2_CDROM Cdrom
)
{
    int nItem = 0, nImage = 0;

    nItem = List->GetItemCount();
    nImage = 0;

    if (Cdrom->bIsDVD) {
        nImage = IDI_DVD - IDI_FLOPPY;
    } else {
        nImage = IDI_CDROM - IDI_FLOPPY;
    }

    nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                              0, 0, nImage, (LONG)Cdrom);

    Ext2RefreshVLCD(List, Cdrom, nItem);
}

VOID
Ext2RefreshVolumeList(CListCtrl *List)
{
    List->DeleteAllItems();
    PEXT2_VOLUME  chain = gVols;

    /* adding disk volumes */
    while (chain) {
        Ext2InsertVolume(List, chain);
        chain = chain->Next;
    }

    /* adding cdroms */

    ULONG   i = 0;

    for (i=0; i < g_nCdroms; i++) {

        if (!gCdroms[i].bLoaded || gCdroms[i].bEjected) {
            continue;
        }
        Ext2InsertCdromAsVolume(List, &gCdroms[i]);
    }
}

VOID
Ext2RefreshDVPT(
    CListCtrl*      List,
    PEXT2_PARTITION Part,
    int             nItem
)
{

    ULONGLONG   totalSize, usedSize;
    CString     sSize, sUnit, s;

    PEXT2_LETTER  first = NULL;
    PEXT2_VOLUME    chain = NULL;

    CString  Letters = Ext2QueryVolumeLetterStrings(
                           Part->DrvLetters, &first);

    List->SetItem( nItem, 0, LVIF_TEXT, (LPCSTR)Letters, 0, 0, 0,0);

    if (Part->Entry) {
        CString PartType;
        if (Part->Entry->PartitionStyle == PARTITION_STYLE_MBR) {
            PartType = PartitionString(Part->Entry->Mbr.PartitionType);
        } else if (Part->Entry->PartitionStyle == PARTITION_STYLE_MBR) {
            PartType = "GPT";
        } else {
            PartType = "RAW";
        }

        List->SetItem(nItem, 6, LVIF_TEXT, (LPCSTR)
                      PartType, 0, 0, 0,0);
    }

    /* query the volume information of the partition */
    chain = Part->Volume;
    if (!chain) {
        PEXT2_DISK  disk = Part->Disk;
        CString s;
        if (disk->SDD.RemovableMedia) {
            s.LoadString(IDS_DISK_TYPE_BASIC);
        } else if (disk->bLoaded) {
            if (disk->Layout) {
                if (disk->Layout->PartitionStyle == PARTITION_STYLE_MBR) {
                    if (disk->Layout->PartitionEntry->Mbr.PartitionType
                            == PARTITION_LDM) {
                        s.LoadString(IDS_DISK_TYPE_DYN);
                    } else {
                        s.LoadString(IDS_DISK_TYPE_BASIC);
                    }
                } else if (disk->Layout->PartitionStyle == PARTITION_STYLE_MBR) {
                    s = "GUID";
                }
            } else {
                s = "RAW";
            }
        } else {
            s = "Stopped";
        }

        List->SetItem(nItem, 1, LVIF_TEXT, (LPCSTR)s, 0, 0, 0,0);
        return;
    }

    if (chain->bDynamic) {
        s.LoadString(IDS_DISK_TYPE_DYN);
    } else {
        s.LoadString(IDS_DISK_TYPE_BASIC);
    }
    List->SetItem(nItem, 1, LVIF_TEXT, (LPCSTR)s, 0, 0, 0,0);

    List->SetItem(nItem, 2, LVIF_TEXT, (LPCSTR)chain->FileSystem, 0, 0, 0,0);

    totalSize = chain->FssInfo.TotalAllocationUnits.QuadPart;
    usedSize  = totalSize - chain->FssInfo.AvailableAllocationUnits.QuadPart;
    totalSize = totalSize * chain->FssInfo.BytesPerSector *
                chain->FssInfo.SectorsPerAllocationUnit;
    usedSize  = usedSize * chain->FssInfo.BytesPerSector *
                chain->FssInfo.SectorsPerAllocationUnit;

    if (totalSize > (ULONGLONG) 10 * 1024 * 1024 * 1024) {
        totalSize = totalSize / ((ULONG)1024 * 1024 * 1024);
        usedSize  = usedSize / ((ULONG)1024 * 1024 * 1024);
        sUnit = " GB";
    } else if (totalSize > 10 * 1024 * 1024) {
        totalSize = totalSize / (1024 * 1024);
        usedSize  = usedSize / (1024 * 1024);
        sUnit = " MB";
    } else if (totalSize > 10 * 1024) {
        totalSize = totalSize / (1024);
        usedSize  = usedSize / (1024);
        sUnit = " KB";
    } else {
        sUnit = " B";
    }

    sSize.Format("%I64u", totalSize);
    sSize += sUnit;
    List->SetItem(nItem, 3, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    if (chain->bRecognized) {
        sSize.Format("%I64u", usedSize);
        sSize += sUnit;
    }
    List->SetItem(nItem, 4, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    List->SetItem(nItem, 5, LVIF_TEXT, (LPCSTR)chain->EVP.Codepage, 0, 0, 0,0);
}

VOID
Ext2InsertPartition(
    CListCtrl*      List,
    PEXT2_DISK      Disk,
    PEXT2_PARTITION Part
)
{
    int nItem = 0, nImage = 0;

    nItem = List->GetItemCount();
    nImage = 0;

    if (!Disk) {
        nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                                  0, 0, nImage, (LONG)NULL);
        return;
    }

    if (!Part) {
        nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                                  0, 0, nImage, (LONG)&Disk->Null);
        List->SetItem( nItem, 1, LVIF_TEXT, (LPCSTR)"RAW", 0, 0, 0,0);
        return;
    }

    nItem = List->InsertItem( LVIF_PARAM| LVIF_IMAGE, nItem, NULL,
                              0, 0, nImage, (LONG)Part);

    Ext2RefreshDVPT(List, Part, nItem);
}

VOID
Ext2InsertDisk(
    CListCtrl *List,
    PEXT2_DISK Disk
)
{
    UCHAR   i;
    CHAR  devName[64];
    int nItem = 0, nImage = 0;

    sprintf(devName, "DISK %d", Disk->OrderNo);
    nItem = List->GetItemCount();
    nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                              0, 0, nImage, (LONG)Disk);
    List->SetItem( nItem, 0, LVIF_TEXT, (LPCSTR)devName, 0, 0, 0,0);

    if (Disk->NumParts > 0) {
        for (i=0; i < Disk->NumParts; i++) {
            Ext2InsertPartition(List, Disk, &Disk->DataParts[i]);
        }
    } else {
        Ext2InsertPartition(List, Disk, NULL);
    }
}


VOID
Ext2RefreshDVCM(
    CListCtrl *List,
    PEXT2_CDROM Cdrom,
    int nItem
)
{
    ULONGLONG   totalSize;
    CString     sSize, sUnit, s;

    PEXT2_LETTER  first = NULL;

    CString  Letters = Ext2QueryVolumeLetterStrings(
                           Cdrom->DrvLetters, &first);

    if (!Cdrom->bLoaded) {
        List->SetItem( nItem, 1, LVIF_TEXT, (LPCSTR)"Stopped", 0, 0, 0,0);
        return;
    }

    List->SetItem( nItem, 0, LVIF_TEXT, (LPCSTR)Letters, 0, 0, 0,0);

    if (Cdrom->bEjected) {
        return;
    }

    s.LoadString(IDS_DISK_TYPE_BASIC);
    List->SetItem(nItem, 1, LVIF_TEXT, (LPCSTR)s, 0, 0, 0,0);
    List->SetItem(nItem, 2, LVIF_TEXT, (LPCSTR)"CDFS", 0, 0, 0,0);

    totalSize = (ULONGLONG)Cdrom->DiskGeometry.Cylinders.QuadPart;
    totalSize = totalSize * Cdrom->DiskGeometry.TracksPerCylinder *
                Cdrom->DiskGeometry.SectorsPerTrack *
                Cdrom->DiskGeometry.BytesPerSector;

    if (totalSize > 1024 * 1024 * 1024) {
        totalSize = totalSize / (1024 * 1024 * 1024);
        sUnit = " GB";
    } else if (totalSize > 1024 * 1024) {
        totalSize = totalSize / (1024 * 1024);
        sUnit = " MB";
    } else if (totalSize > 1024) {
        totalSize = totalSize / (1024);
        sUnit = " KB";
    } else {
        sUnit = " B";
    }

    sSize.Format("%I64u", totalSize);
    sSize += sUnit;
    List->SetItem(nItem, 3, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    sSize.Format("%I64u", totalSize);
    sSize += sUnit;
    List->SetItem(nItem, 4, LVIF_TEXT, (LPCSTR)sSize, 0, 0, 0,0);

    List->SetItem(nItem, 5, LVIF_TEXT, (LPCSTR)"", 0, 0, 0,0);
    List->SetItem(nItem, 6, LVIF_TEXT, (LPCSTR)"", 0, 0, 0,0);
}

VOID
Ext2InsertCdromAsDisk(
    CListCtrl *List,
    PEXT2_CDROM Cdrom
)
{
    CHAR  devName[64];
    int nItem = 0, nImage = 0;

    sprintf(devName, "CDROM %d", Cdrom->OrderNo);
    nItem = List->GetItemCount();
    nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                              0, 0, nImage, (LONG)Cdrom);
    List->SetItem( nItem, 0, LVIF_TEXT, (LPCSTR)devName, 0, 0, 0,0);

    nItem = List->GetItemCount();
    nImage = 0;

    if (Cdrom->bIsDVD) {
        nImage = IDI_DVD - IDI_FLOPPY;
    } else {
        nImage = IDI_CDROM - IDI_FLOPPY;
    }

    nItem = List->InsertItem( LVIF_PARAM|LVIF_IMAGE, nItem, NULL,
                              0, 0, nImage, (LONG)(&Cdrom->Magic[1]));

    Ext2RefreshDVCM(List, Cdrom, nItem);
}

VOID
Ext2RefreshDiskList(CListCtrl *List)
{
    List->DeleteAllItems();
    ULONG   i = 0;

    /* adding disks */
    for (i=0; i < g_nDisks; i++) {
        Ext2InsertDisk(List, &gDisks[i]);
        Ext2InsertPartition(List, NULL, NULL);
    }

    /* adding cdroms */
    for (i=0; i < g_nCdroms; i++) {
        Ext2InsertCdromAsDisk(List, &gCdroms[i]);
        Ext2InsertPartition(List, NULL, NULL);
    }
}


VOID
Ext2CleanupVolumes()
{
    PEXT2_VOLUME  chain = gVols, next = NULL;

    while (chain) {
        next = chain->Next;
        if (chain->Extent) {
            free(chain->Extent);
        }
        free(chain);
        g_nVols--;
        chain = next;
    }

    ASSERT(g_nVols == 0);
    g_nVols = 0;
    gVols = NULL;
}

VOID
Ext2LoadDrvLetter(PEXT2_LETTER drvLetter, CHAR cLetter)
{
    if (drvLetter->bUsed) {
        return;
    }

    if (cLetter >= 'a' && cLetter <= 'z') {
        cLetter -= 0x20;
    }

    memset(drvLetter, 0, sizeof(EXT2_LETTER));
    drvLetter->Letter = cLetter;
    Ext2QueryDrvLetter(drvLetter);
}

VOID
Ext2LoadDrvLetters()
{
    int     i;

    for (i=0; i < 36; i++) {

        PEXT2_LETTER    drvLetter = NULL;
        CHAR            cLetter = 0;

        if (i < 10) {
            drvLetter = &drvDigits[i];
            cLetter = '0' + i;
        } else {
            drvLetter = &drvLetters[i-10];
            cLetter = 'A' + (i - 10);
        }

        Ext2LoadDrvLetter(drvLetter, cLetter);
    }
}

VOID
Ext2CleanDrvLetter(PEXT2_LETTER drvLetter)
{
    if (drvLetter->Extent) {
        free(drvLetter->Extent);
        drvLetter->Extent = NULL;
    }

    if (drvLetter->SDN) {
        free(drvLetter->SDN);
        drvLetter->SDN = NULL;
    }

    drvLetter->bUsed = FALSE;
    drvLetter->bTemporary = FALSE;
    drvLetter->DrvType = DRIVE_NO_ROOT_DIR;
    memset(drvLetter->SymLink, 0, MAX_PATH);
}

VOID
Ext2CleanupDrvLetters()
{
    int i = 0;

    for (i=0; i < 10; i++) {
        Ext2CleanDrvLetter(&drvDigits[i]);
    }

    for (i=0; i < 26; i++) {
        Ext2CleanDrvLetter(&drvLetters[i]);
    }
}

VOID
Ext2DrvNotify(UCHAR drive, int mount)
{
    DEV_BROADCAST_VOLUME    dbv;
    DWORD target = BSM_APPLICATIONS;
    unsigned long drv = 0;

    if (drive >= 'A' && drive <= 'Z')
        drv = drive - 'A';
    else if (drive >= 'a' && drive <= 'z')
        drv = drive - 'a';
    else
        return;

    dbv.dbcv_size       = sizeof( dbv );
    dbv.dbcv_devicetype = DBT_DEVTYP_VOLUME;
    dbv.dbcv_reserved   = 0;
    dbv.dbcv_unitmask   = (1 << drv);
    dbv.dbcv_flags      = DBTF_NET;
    BroadcastSystemMessage(BSF_IGNORECURRENTTASK | BSF_FORCEIFHUNG |
                           BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG,
                           &target, WM_DEVICECHANGE,mount ?
                           DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE,
                           (LPARAM)(DEV_BROADCAST_HDR *)&dbv );
}

BOOLEAN
Ext2RemoveDrvLetter(
    PEXT2_LETTER   drvLetter
)
{
    CHAR drvPath[] = "A:\\\0";
    CHAR dosPath[] = "A:\0";

    BOOL rc = FALSE;

    if (!drvLetter->bUsed) {
        return TRUE;
    }

    dosPath[0] = drvPath[0] = drvLetter->Letter;

    if (drvLetter->bTemporary) {

        rc = DefineDosDevice (
                 DDD_RAW_TARGET_PATH|
                 DDD_REMOVE_DEFINITION|
                 DDD_EXACT_MATCH_ON_REMOVE,
                 dosPath, drvLetter->SymLink);
        DeleteVolumeMountPoint(drvPath);

    } else {

        DefineDosDevice (
            DDD_RAW_TARGET_PATH|
            DDD_REMOVE_DEFINITION|
            DDD_EXACT_MATCH_ON_REMOVE,
            dosPath, drvLetter->SymLink);
        rc = DeleteVolumeMountPoint(drvPath);
    }

    Ext2DrvNotify(drvLetter->Letter, FALSE);

    if (!rc) {
        return FALSE;
    }

    return TRUE;
}

CHAR
Ext2QueryRegistryMountPoint (
    CHAR * devName
)
{
    DWORD    i = 0;

    LONG    status;
    HKEY    hKey;

    DWORD   drvSize;
    DWORD   dosSize;
    DWORD   valType;

    CHAR    keyPath[MAX_PATH];
    CHAR    drvPath[MAX_PATH];
    CHAR    dosPath[64];

    /* set dos deivce path */
    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\DOS Devices");

    /* open session manager\dos devices */
    status = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                            keyPath,
                            0,
                            KEY_ALL_ACCESS,
                            &hKey) ;
    if (status != ERROR_SUCCESS) {
        return FALSE;
    }


    /* enum all values and compare devName ... */
    while (status != ERROR_NO_MORE_ITEMS && i < 1024) {

        valType = REG_SZ;
        dosSize = 64;
        drvSize = MAX_PATH;

        status = RegEnumValue(hKey,
                              i++,
                              &dosPath[0],
                              &dosSize,
                              NULL,
                              &valType,
                              (LPBYTE)&drvPath[0],
                              &drvSize);

        if (status == ERROR_SUCCESS) {
            if (stricmp(devName, drvPath) == 0) {
                RegCloseKey(hKey);
                return dosPath[0];
            }
        }
    }

    RegCloseKey(hKey);

    return 0;
}

BOOLEAN
Ext2SetRegistryMountPoint (
    CHAR * dosPath,
    CHAR * devName,
    BOOLEAN bSet
)
{
    LONG    status;
    HKEY    hKey;
    CHAR    keyPath[MAX_PATH];
    CHAR    drvPath[MAX_PATH];

    /* set dos driver name */
    sprintf(drvPath, "%c:", dosPath[0]);

    /* set dos deivce path */
    strcpy (keyPath, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\DOS Devices");

    /* open session manager\dos devices */
    status = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                            keyPath,
                            0,
                            KEY_ALL_ACCESS,
                            &hKey) ;
    if (status != ERROR_SUCCESS) {
        return FALSE;
    }

    if (bSet) {
        /* set value */
        status = RegSetValueEx(
                     hKey, drvPath,
                     0, REG_SZ,
                     (BYTE *)devName,
                     strlen(devName)
                 );
    } else {
        /* delete key */
        status = RegDeleteValue(
                     hKey, drvPath
                 );
    }

    RegCloseKey(hKey);

    if (status != ERROR_SUCCESS) {
        return FALSE;
    }

    return TRUE;
}

VOID
Ext2UpdateDrvLetter(
    PEXT2_LETTER   drvLetter,
    PCHAR          devPath
)
{
    CHAR dosPath[] = "A:\0";
    BOOL rc = 0;

    if (drvLetter->bUsed) {
        return;
    }

    dosPath[0] = drvLetter->Letter;
    rc = DefineDosDevice(DDD_RAW_TARGET_PATH,
                         dosPath, devPath);

    if (rc) {
        DefineDosDevice (
            DDD_RAW_TARGET_PATH|
            DDD_REMOVE_DEFINITION|
            DDD_EXACT_MATCH_ON_REMOVE,
            dosPath, devPath);
    }
}

BOOLEAN
Ext2AssignDrvLetter(
    PEXT2_LETTER   drvLetter,
    PCHAR          devPath,
    BOOLEAN        bMountMgr
)
{
    CHAR dosPath[] = "A:\0";
    CHAR drvPath[] = "A:\\\0";
    CHAR volumeName[MAX_PATH];

    BOOL rc = 0;

    if (drvLetter->bUsed) {
        return FALSE;
    }

    memset(volumeName, 0, MAX_PATH);
    dosPath[0] = drvPath[0] = drvLetter->Letter;

    rc = DefineDosDevice(DDD_RAW_TARGET_PATH,
                         dosPath, devPath);

    if (rc) {

        /* Now it's done, since user only
           needs a temporary mount point */
        if (!bMountMgr) {
            goto DrvMounted;
        }

        rc = GetVolumeNameForVolumeMountPoint (
                 drvPath, volumeName, MAX_PATH);

        DefineDosDevice (
            DDD_RAW_TARGET_PATH|
            DDD_REMOVE_DEFINITION|
            DDD_EXACT_MATCH_ON_REMOVE,
            dosPath, devPath);

        if (rc) {
            rc = SetVolumeMountPoint(drvPath, volumeName);
        }
    }

    if (!rc) {
        return FALSE;
    }

DrvMounted:

    if (rc)
        Ext2DrvNotify(drvLetter->Letter, TRUE);
    drvLetter->bTemporary = !bMountMgr;
    Ext2QueryDrvLetter(drvLetter);

    return TRUE;
}

/*
 *  Mount Manager Support Routines
 */

VOID
Ext2AnsiToUnicode(
    CHAR *      AnsiName,
    WCHAR*      UniName,
    USHORT      UniLength
)
{
    USHORT                  NameLength = 0;
    NT::ANSI_STRING         AnsiFilespec;
    NT::UNICODE_STRING      UnicodeFilespec;

    memset(UniName, 0, sizeof(WCHAR) * UniLength);

    NameLength = strlen(AnsiName);
    ASSERT(NameLength < UniLength);

    AnsiFilespec.MaximumLength = AnsiFilespec.Length = NameLength;
    AnsiFilespec.Buffer = AnsiName;

    UnicodeFilespec.MaximumLength = UniLength * 2;
    UnicodeFilespec.Length = 0;
    UnicodeFilespec.Buffer = (PWSTR)UniName;

    NT::RtlAnsiStringToUnicodeString(&UnicodeFilespec, &AnsiFilespec, FALSE);
}

BOOL
Ext2VolumeArrivalNotify(PCHAR  VolumePath)
{
    HANDLE              hMountMgr = NULL;

    NT::IO_STATUS_BLOCK iosb;
    NT::NTSTATUS        status;
    DWORD               rc = 0;

    WCHAR      volPath[MAX_PATH + 2];
    PMOUNTMGR_TARGET_NAME target;

    target = (PMOUNTMGR_TARGET_NAME) volPath;
    Ext2AnsiToUnicode(VolumePath, &target->DeviceName[0], MAX_PATH);
    target->DeviceNameLength = (USHORT)wcslen(target->DeviceName) * 2;

    status = Ext2Open("\\Device\\MountPointManager", &hMountMgr, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    status = NT::ZwDeviceIoControlFile(
                 hMountMgr, NULL, NULL, NULL, &iosb,
                 IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
                 (PVOID)target, sizeof(USHORT) +
                 target->DeviceNameLength, NULL, 0
             );

    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

errorout:

    Ext2Close(&hMountMgr);
    return NT_SUCCESS(status);
}

BOOLEAN
Ext2SetVolumeMountPoint (
    CHAR * dosPath,
    CHAR * devName
)
{
    HANDLE              hMountMgr;

    NT::IO_STATUS_BLOCK iosb;
    NT::NTSTATUS        status;
    DWORD               rc = 0;

    CHAR       dosDevice[MAX_PATH];
    WCHAR      volPath[MAX_PATH];
    WCHAR      devPath[MAX_PATH];

    PMOUNTMGR_CREATE_POINT_INPUT  mcpi = NULL;

    USHORT   lmcpi = 0, lvolp = 0, ldevp = 0;

    memset(dosDevice, 0, MAX_PATH);
    memset(volPath, 0, MAX_PATH * sizeof(WCHAR));
    memset(devPath, 0, MAX_PATH * sizeof(WCHAR));

    /* covert names to unicode */
    sprintf(dosDevice, "\\DosDevices\\%c:", toupper(dosPath[0]));
    Ext2AnsiToUnicode(dosDevice, volPath, MAX_PATH);
    Ext2AnsiToUnicode(devName, devPath, MAX_PATH);

    ldevp = (USHORT)wcslen(devPath) * 2;
    lvolp = (USHORT)wcslen(volPath) * 2;
    lmcpi = sizeof(MOUNTMGR_CREATE_POINT_INPUT) + lvolp + ldevp;

    /* initialize MMGR_CREATE_MOUNT_POINT_INPUT */
    mcpi = (PMOUNTMGR_CREATE_POINT_INPUT) malloc(lmcpi);
    if (mcpi == NULL) {
        status = STATUS_UNSUCCESSFUL;
        goto errorout;
    }

    mcpi->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
    mcpi->SymbolicLinkNameLength = lvolp;
    mcpi->DeviceNameOffset = mcpi->SymbolicLinkNameOffset + lvolp;
    mcpi->DeviceNameLength = ldevp;

    memcpy((PCHAR)mcpi + mcpi->SymbolicLinkNameOffset,
           volPath, lvolp);
    memcpy((PCHAR)mcpi + mcpi->DeviceNameOffset,
           devPath, ldevp);

    /* open MountMgr */
    status = Ext2Open("\\Device\\MountPointManager", &hMountMgr, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    /* ioctl .... */
    status = NT::ZwDeviceIoControlFile(
                 hMountMgr, NULL, NULL, NULL, &iosb,
                 IOCTL_MOUNTMGR_CREATE_POINT,
                 (PVOID)mcpi, lmcpi, NULL, 0
             );

    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    Ext2Close(&hMountMgr);

errorout:
    if (mcpi) {
        free(mcpi);
    }

    return NT_SUCCESS(status);
}


UCHAR
Ext2QueryMountPoint(
    CHAR *      volume
)
{
    NT::IO_STATUS_BLOCK     iosb;
    NT::NTSTATUS            status;
    ULONG                   lp, lps;
    ULONG                   i;
    HANDLE                  hMountMgr = 0;

    NT::UNICODE_STRING      volPoint;
    NT::UNICODE_STRING      VolumeName;
    WCHAR                   VolumeBuffer[MAX_PATH];

    PMOUNTMGR_MOUNT_POINT   point = NULL;
    PMOUNTMGR_MOUNT_POINTS  points = NULL;
    UCHAR                   drvLetter = 0;

    memset(VolumeBuffer, 0, MAX_PATH * sizeof(WCHAR));
    Ext2AnsiToUnicode(volume, VolumeBuffer, MAX_PATH);

    VolumeName.MaximumLength = MAX_PATH;
    VolumeName.Length = (USHORT)wcslen(VolumeBuffer) * 2;
    VolumeName.Buffer = VolumeBuffer;

    status = Ext2Open("\\Device\\MountPointManager", &hMountMgr, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    lp = VolumeName.Length + sizeof(MOUNTMGR_MOUNT_POINT) + 2;
    point = (PMOUNTMGR_MOUNT_POINT)malloc(lp);
    if (point == NULL) {
        goto errorout;
    }

    /* initialize MountMgr ioctl input structure */
    memset(point, 0, lp);
    point->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
    point->DeviceNameLength = VolumeName.Length;
    RtlMoveMemory((PCHAR) point + point->DeviceNameOffset,
                  VolumeBuffer, VolumeName.Length);
    lps = 1024;

Again:

    /* allocate ioctl output structure */
    points = (PMOUNTMGR_MOUNT_POINTS)malloc(lps);
    if (!points) {
        goto errorout;
    }
    RtlZeroMemory(points, lps);

    /* could MoungMgr to create volume points for us ? */
    status = NT::ZwDeviceIoControlFile(
                 hMountMgr, NULL, NULL, NULL, &iosb,
                 IOCTL_MOUNTMGR_QUERY_POINTS,
                 point, lp, points, lps
             );

    if (status == STATUS_BUFFER_OVERFLOW) {
        lps = (ULONG)iosb.Information + 2;
        free(points);
        points = NULL;
        goto Again;
    }

    for (i = 0; i < points->NumberOfMountPoints; i++) {

        volPoint.Length = volPoint.MaximumLength =
                              points->MountPoints[i].SymbolicLinkNameLength;
        volPoint.Buffer = (PWSTR) ((PCHAR) points +
                                   points->MountPoints[i].SymbolicLinkNameOffset);

#if 0
        if (MOUNTMGR_IS_VOLUME_NAME(&volPoint)) {
            if (length) {
                if (*length >= volPoint.Length + 2) {
                    *length = volPoint.Length + 2;
                    memcpy(vp, volPoint.Buffer, volPoint.Length);
                    vp[1] = (WCHAR)'\\';
                    vp[volPoint.Length / 2] = (WCHAR)'\\';
                    vp[*length / 2] = 0;
                } else {
                    *length = 0;
                }
            }
        }
#endif

        if (MOUNTMGR_IS_DRIVE_LETTER(&volPoint)) {
            drvLetter = (UCHAR)(volPoint.Buffer[12]);
            break;
        }
    }

errorout:

    if (points) {
        free(points);
    }

    if (point) {
        free(point);
    }

    if (hMountMgr) {
        Ext2Close(&hMountMgr);
    }

    return drvLetter;
}

PEXT2_LETTER
Ext2GetDrvLetterPoint(CHAR drvChar)
{
    PEXT2_LETTER    drvLetter = NULL;

    if (drvChar >= '0' && drvChar <= '9') {
        drvLetter = &drvDigits[drvChar - '0'];
    } else if (drvChar >= 'A' && drvChar <= 'Z') {
        drvLetter = &drvLetters[drvChar - 'A'];
    } else {
        drvLetter = NULL;
    }

    return drvLetter;
}

BOOL
Ext2InsertMountPoint(
    CHAR * volume,
    UCHAR drvChar,
    BOOL  bGlobal
)
{
    CHAR volumeName[MAX_PATH];

    CHAR dosPath[] = "A:\0";
    CHAR drvPath[] = "A:\\\0";
    BOOLEAN rc = FALSE;

    dosPath[0] = drvPath[0] = drvChar & 0x7F;
    rc = DefineDosDevice(DDD_RAW_TARGET_PATH,
                         dosPath, volume);

    if (rc && bGlobal) {
        rc = GetVolumeNameForVolumeMountPoint (
                 drvPath, volumeName, MAX_PATH);

        DefineDosDevice (
            DDD_RAW_TARGET_PATH|
            DDD_REMOVE_DEFINITION,
            dosPath, volume);
        if (rc) {
            rc = SetVolumeMountPoint(drvPath, volumeName);
        }
        if (rc)
            Ext2DrvNotify((drvChar & 0x7F), TRUE);
    }

    return rc;
}

VOID
Ext2RemoveMountPoint(
    PEXT2_LETTER    drvLetter,
    BOOLEAN         bPermanent
)
{
    CHAR drvPath[] = "A:\\\0";
    CHAR dosPath[] = "A:\0";

    dosPath[0] = drvPath[0] = drvLetter->Letter;

    DefineDosDevice (
        DDD_RAW_TARGET_PATH|
        DDD_REMOVE_DEFINITION|
        DDD_EXACT_MATCH_ON_REMOVE,
        dosPath, drvLetter->SymLink);
    if (bPermanent) {
        DeleteVolumeMountPoint(drvPath);
    }

    Ext2DrvNotify(drvLetter->Letter, FALSE);
    Ext2CleanDrvLetter(drvLetter);
    Ext2QueryDrvLetter(drvLetter);
}

BOOL
Ext2RefreshVolumePoint(
    CHAR *          volume,
    UCHAR           drvChar
)
{
    PEXT2_LETTER    drvLetter = NULL;
    BOOLEAN         rc = TRUE;
    CHAR            cLetter = 0;

    cLetter = Ext2QueryMountPoint(volume);
    if (cLetter) {
        goto errorout;
    } else {
        rc = FALSE;
    }

    if (drvChar & 0x7F) {
        if (Ext2InsertMountPoint(volume, drvChar & 0x7F, TRUE)) {
            return TRUE;
        }
    }

    if (!rc) {
        Ext2VolumeArrivalNotify(volume);
    }

    cLetter = Ext2QueryMountPoint(volume);
    if (!cLetter && (cLetter != (drvChar & 0x7F))) {
        Ext2InsertMountPoint(volume, drvChar & 0x7F, TRUE);
    }

    cLetter = Ext2QueryMountPoint(volume);
    if (cLetter) {
        Ext2InsertMountPoint(volume, cLetter, FALSE);
    }

    rc =  (cLetter > 0) ?  TRUE: FALSE;

errorout:

    return rc;
}

BOOL
Ext2NotifyVolumePoint(
    PEXT2_VOLUME    volume,
    UCHAR           drvChar
)
{
    BOOL rc = FALSE;
    PCHAR  Name = NULL;

    if (volume->Part) {
        Name = &volume->Part->Name[0];
    } else {
        Name = &volume->Name[0];
    }
    rc = Ext2VolumeArrivalNotify(Name);
    Sleep(1000);

    drvChar = Ext2QueryMountPoint(Name);
    if (drvChar) {
        Ext2InsertMountPoint(Name, drvChar, FALSE);
        rc = TRUE;
    }

    return rc;
}

#define EXT2_MANAGER_NAME  "Ext2 Volume Manager"

BOOL
Ext2SetAppAutorun(BOOL bInstall)
{
    BOOL   rc = FALSE;
    HKEY   key ;
    CHAR   keyPath[MAX_PATH] ;
    LONG   status ;

    CHAR   	appPath[MAX_PATH] ;

    GetModuleFileName(NULL, appPath, MAX_PATH - 6);
    strcat(appPath, " -quiet");

    strcpy (keyPath, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run") ;
    status = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                           keyPath,
                           0,
                           KEY_ALL_ACCESS,
                           &key) ;

    if (status != ERROR_SUCCESS) {
        return FALSE;
    }

    if (!bInstall) {
        status = RegDeleteValue (key, EXT2_MANAGER_NAME) ;
        if (status != ERROR_SUCCESS) {
            goto errorout;
        }
    } else {
        status = RegSetValueEx( key, EXT2_MANAGER_NAME, 0, REG_SZ,
                                (BYTE *)appPath, strlen(appPath));
        if (status != ERROR_SUCCESS) {
            goto errorout;
        }
    }

    rc = TRUE;

errorout:

    RegCloseKey (key) ;
    return rc;
}

#define SERVICE_CMD_LENGTH (MAX_PATH * 2)

int
Ext2SetManagerAsService(BOOL bInstall)
{
    CHAR Target[SERVICE_CMD_LENGTH];
    SC_HANDLE   hService;
    SC_HANDLE   hManager;

    // get the filename of this executable
    if (GetModuleFileName(NULL, Target, SERVICE_CMD_LENGTH - 20) == 0) {
        AfxMessageBox("Unable to install Ext2Mgr as service",
                      MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
    }

    // append parameters to the end of the path:
    strcat(Target, " -service -hide");

    // open Service Control Manager
    hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hManager == NULL) {
        AfxMessageBox("Ext2Mgr: cannot open Service Control Manager",
                      MB_ICONEXCLAMATION | MB_OK);
        return FALSE;
    }

    if (bInstall) {

        // now create service entry for Ext2Mgr
        hService = CreateService(
                       hManager,                   // SCManager database
                       "Ext2Mgr",                  // name of service
                       "Ext2 Volume Manger",       // name to display
                       SERVICE_ALL_ACCESS,	        // desired access
                       SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
                       // service type
                       SERVICE_AUTO_START,	        // start type
                       SERVICE_ERROR_NORMAL,       // error control type
                       Target,	                    // service's binary
                       NULL,                       // no load ordering group
                       NULL,                       // no tag identifier
                       NULL,			            // dependencies
                       NULL,						// LocalSystem account
                       NULL);                      // no password

        if (hService == NULL) {
            DWORD error = GetLastError();
            if (error == ERROR_SERVICE_EXISTS) {
                AfxMessageBox("Ext2Mgr service is already registered.",
                              MB_ICONEXCLAMATION | MB_OK);
            } else {
                AfxMessageBox("Ext2Mgr service couldn't be registered.",
                              MB_ICONEXCLAMATION | MB_OK);
            }
        } else {

            CloseServiceHandle(hService);

            // got Ext2Mgr installed as a service
            AfxMessageBox(
                "Ext2Mgr service was successfully registered. \n\n"
                "You can modify the default settings and start/stop it from Control Panel.\n"
                "The service will automatically run the next time when system is restarted.\n",
                MB_ICONINFORMATION | MB_OK);
        }

        Ext2SetAppAutorun(FALSE);

    } else {

        /* open the service of Ext2Mgr */
        hService = OpenService(
                       hManager,
                       "Ext2Mgr",
                       SERVICE_ALL_ACCESS
                   );

        if (hService != NULL) {

#if 0
            SERVICE_STATUS status;

            // stop the service
            if (ControlService(hService, SERVICE_CONTROL_STOP, &status)) {

                while (QueryServiceStatus(hService, &status)) {
                    if (status.dwCurrentState == SERVICE_STOP_PENDING) {
                        Sleep(1000);
                    } else {
                        break;
                    }
                }

                if (status.dwCurrentState != SERVICE_STOPPED) {
                    AfxMessageBox("Could not stop Ext2Mgr service !",
                                  MB_ICONEXCLAMATION | MB_OK);
                }
            }
#endif
            // remove the service from the SCM
            if (DeleteService(hService)) {
                AfxMessageBox("The Ext2Mgr service has been unregistered.",
                              MB_ICONINFORMATION | MB_OK);
            } else {
                DWORD error = GetLastError();
                if (error == ERROR_SERVICE_MARKED_FOR_DELETE) {
                    AfxMessageBox("Ext2Mgr service is already unregistered",
                                  MB_ICONEXCLAMATION | MB_OK);
                } else {
                    AfxMessageBox("Ext2Mgr service could not be unregistered",
                                  MB_ICONEXCLAMATION | MB_OK);
                }
            }

            CloseServiceHandle(hService);
        }
    }

    CloseServiceHandle(hManager);

    return TRUE;
}

BOOL g_bAutoRemoveDeadLetters = TRUE;

VOID
Ext2AutoRemoveDeadLetters()
{
    ULONGLONG   LetterMask = Ext2DrvLetters[0];
    DWORD       i, j;
    PEXT2_DISK      disk;
    PEXT2_PARTITION part;
    PEXT2_CDROM     cdrom;

    for (i = 0; i < g_nDisks; i++) {
        disk = &gDisks[i];
        if (disk->DataParts == NULL) {
            continue;
        }
        for (j=0; j < disk->NumParts; j++) {
            part = &disk->DataParts[j];
            if (part) {
                LetterMask &= ~(part->DrvLetters);
            }
        }
    }

    for (i = 0; i < g_nCdroms; i++) {
        cdrom = &gCdroms[i];
        LetterMask &= ~(cdrom->DrvLetters);
    }

    for (i=0; i < 10; i++) {
        if (drvDigits[i].bUsed && (drvDigits[i].Extent == NULL) &&
                (LetterMask & (((ULONGLONG) 1) << (i + 32)) ) ) {
            if (drvDigits[i].DrvType == DRIVE_FIXED) {
                LetterMask &= (~(((ULONGLONG) 1) << (i + 32)));
                Ext2RemoveMountPoint(&drvDigits[i], FALSE);
                Ext2RemoveDosSymLink(drvDigits[i].Letter);
            }
        }
    }

    for (i=2; i <26; i++) {
        if (drvLetters[i].bUsed && (drvLetters[i].Extent == NULL) &&
                (LetterMask & (((ULONGLONG) 1) << i)) ) {
            if (drvLetters[i].DrvType == DRIVE_FIXED) {
                LetterMask &= (~(((ULONGLONG) 1) << i));
                Ext2RemoveMountPoint(&drvLetters[i], FALSE);
                Ext2RemoveDosSymLink(drvLetters[i].Letter);
            }
        }
    }

    Ext2DrvLetters[0] = LetterMask;
}

BOOLEAN
Ext2RemoveDosSymLink(CHAR drvChar)
{
    EXT2_MOUNT_POINT             E2MP;
    NT::NTSTATUS                 status;
    HANDLE                       handle = NULL;
    NT::IO_STATUS_BLOCK          iosb;

    memset(&E2MP, 0, sizeof(EXT2_MOUNT_POINT));
    E2MP.Magic = EXT2_APP_MOUNTPOINT_MAGIC;
    E2MP.Command = APP_CMD_DEL_DOS_SYMLINK;
    E2MP.Link[0] = (USHORT) drvChar;

    status = Ext2Open("\\DosDevices\\Ext2Fsd",
                      &handle, EXT2_DESIRED_ACCESS);
    if (!NT_SUCCESS(status)) {
        goto errorout;
    }

    status = NT::ZwDeviceIoControlFile(
                 handle, NULL, NULL, NULL, &iosb,
                 IOCTL_APP_MOUNT_POINT,
                 &E2MP, sizeof(EXT2_MOUNT_POINT),
                 &E2MP, sizeof(EXT2_MOUNT_POINT)
             );

errorout:

    Ext2Close(&handle);

    return NT_SUCCESS(status);
}